// deno-fmt-ignore-file
// deno-lint-ignore-file
// This code was bundled using `deno bundle` and it's not recommended to edit it manually

class APIError extends Error {
    response;
    json;
    constructor({ response , json , message  }){
        super(message);
        this.response = response;
        this.json = json;
    }
}
const AGENT_NAME = "s3si.ts";
const S3SI_VERSION = "0.4.5";
const NSOAPP_VERSION = "2.6.0";
const WEB_VIEW_VERSION = "4.0.0-d5178440";
var Queries;
(function(Queries) {
    Queries["HomeQuery"] = "7dcc64ea27a08e70919893a0d3f70871";
    Queries["LatestBattleHistoriesQuery"] = "0d90c7576f1916469b2ae69f64292c02";
    Queries["RegularBattleHistoriesQuery"] = "3baef04b095ad8975ea679d722bc17de";
    Queries["BankaraBattleHistoriesQuery"] = "0438ea6978ae8bd77c5d1250f4f84803";
    Queries["XBattleHistoriesQuery"] = "6796e3cd5dc3ebd51864dc709d899fc5";
    Queries["EventBattleHistoriesQuery"] = "9744fcf676441873c7c8a51285b6aa4d";
    Queries["PrivateBattleHistoriesQuery"] = "8e5ae78b194264a6c230e262d069bd28";
    Queries["VsHistoryDetailQuery"] = "9ee0099fbe3d8db2a838a75cf42856dd";
    Queries["CoopHistoryQuery"] = "91b917becd2fa415890f5b47e15ffb15";
    Queries["CoopHistoryDetailQuery"] = "379f0d9b78b531be53044bcac031b34b";
    Queries["myOutfitCommonDataFilteringConditionQuery"] = "d02ab22c9dccc440076055c8baa0fa7a";
    Queries["myOutfitCommonDataEquipmentsQuery"] = "d29cd0c2b5e6bac90dd5b817914832f8";
    Queries["HistoryRecordQuery"] = "d9246baf077b2a29b5f7aac321810a77";
    Queries["ConfigureAnalyticsQuery"] = "f8ae00773cc412a50dd41a6d9a159ddd";
})(Queries || (Queries = {}));
const S3SI_LINK = "https://github.com/spacemeowx2/s3si.ts";
const USERAGENT = `${AGENT_NAME}/${S3SI_VERSION} (${S3SI_LINK})`;
const DEFAULT_APP_USER_AGENT = "Mozilla/5.0 (Linux; Android 11; Pixel 5) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/94.0.4606.61 Mobile Safari/537.36";
const SPLATNET3_ENDPOINT = "https://api.lp1.av5ja.srv.nintendo.net/api/graphql";
const BATTLE_NAMESPACE = "b3a2dbf5-2c09-4792-b78c-00b548b70aeb";
const COOP_NAMESPACE = "f1911910-605e-11ed-a622-7085c2057a9d";
const S3SI_NAMESPACE = "63941e1c-e32e-4b56-9a1d-f6fbe19ef6e1";
const SPLATNET3_STATINK_MAP = {
    RULE: {
        TURF_WAR: "nawabari",
        AREA: "area",
        LOFT: "yagura",
        GOAL: "hoko",
        CLAM: "asari",
        TRI_COLOR: "tricolor"
    },
    RESULT: {
        WIN: "win",
        LOSE: "lose",
        DEEMED_LOSE: "lose",
        EXEMPTED_LOSE: "exempted_lose",
        DRAW: "draw"
    },
    DRAGON: {
        NORMAL: undefined,
        DECUPLE: "10x",
        DRAGON: "100x",
        DOUBLE_DRAGON: "333x"
    },
    COOP_EVENT_MAP: {
        1: "rush",
        2: "goldie_seeking",
        3: "griller",
        4: "mothership",
        5: "fog",
        6: "cohock_charge",
        7: "giant_tornado",
        8: "mudmouth_eruption"
    },
    COOP_SPECIAL_MAP: {
        "bd327d1b64372dedefd32adb28bea62a5b6152d93aada5d9fc4f669a1955d6d4": "nicedama",
        "463eedc60013608666b260c79ac8c352f9795c3d0cce074d3fbbdbd2c054a56d": "hopsonar",
        "fa8d49e8c850ee69f0231976208a913384e73dc0a39e6fb00806f6aa3da8a1ee": "megaphone51",
        "252059408283fbcb69ca9c18b98effd3b8653ab73b7349c42472281e5a1c38f9": "jetpack",
        "680379f8b83e5f9e033b828360827bc2f0e08c34df1abcc23de3d059fe2ac435": "kanitank",
        "0785cb4979024a83aaa2196e287e232d5d7e4ac959895a650c30ed00fedbc714": "sameride",
        "380e541b5bc5e49d77ff1a616f1343aeba01d500fee36aaddf8f09d74bd3d3bc": "tripletornado"
    },
    WATER_LEVEL_MAP: {
        0: "low",
        1: "normal",
        2: "high"
    }
};
const CONTROL_CHARS = /[\x00-\x1F\x7F]/;
const COOKIE_NAME_BLOCKED = /[()<>@,;:\\"/[\]?={}]/;
const COOKIE_OCTET_BLOCKED = /[\s",;\\]/;
const COOKIE_OCTET = /^[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]+$/;
const TERMINATORS = [
    "\n",
    "\r",
    "\0"
];
function isSameDomainOrSubdomain(domainA, domainB) {
    if (!domainA || !domainB) {
        return false;
    }
    let longerDomain;
    let shorterDomain;
    if (domainB.length > domainA.length) {
        longerDomain = domainB;
        shorterDomain = domainA;
    } else {
        longerDomain = domainA;
        shorterDomain = domainB;
    }
    const indexOfDomain = longerDomain.indexOf(shorterDomain);
    if (indexOfDomain === -1) {
        return false;
    } else if (indexOfDomain > 0) {
        if (longerDomain.charAt(indexOfDomain - 1) !== ".") {
            return false;
        }
    }
    return true;
}
function trimTerminator(str) {
    if (str === undefined || str === "") return str;
    for(let t = 0; t < TERMINATORS.length; t++){
        const terminatorIdx = str.indexOf(TERMINATORS[t]);
        if (terminatorIdx !== -1) {
            str = str.substr(0, terminatorIdx);
        }
    }
    return str;
}
function isValidName(name) {
    if (!name) {
        return false;
    }
    if (CONTROL_CHARS.test(name) || COOKIE_NAME_BLOCKED.test(name)) {
        return false;
    }
    return true;
}
function trimWrappingDoubleQuotes(val) {
    if (val.length >= 2 && val.at(0) === '"' && val.at(-1) === '"') {
        return val.slice(1, -1);
    }
    return val;
}
function isValidValue(val) {
    if (val === "") {
        return true;
    }
    if (!val) {
        return false;
    }
    if (CONTROL_CHARS.test(val) || COOKIE_OCTET_BLOCKED.test(val) || !COOKIE_OCTET.test(val)) {
        return false;
    }
    return true;
}
function parseURL(input) {
    let copyUrl;
    if (input instanceof Request) {
        copyUrl = input.url;
    } else if (input instanceof URL) {
        copyUrl = input.toString();
    } else {
        copyUrl = input;
    }
    copyUrl = copyUrl.replace(/^\./, "");
    if (!copyUrl.includes("://")) {
        copyUrl = "http://" + copyUrl;
    }
    return new URL(copyUrl);
}
class Cookie {
    name;
    value;
    path;
    domain;
    expires;
    maxAge;
    secure;
    httpOnly;
    sameSite;
    creationDate = Date.now();
    creationIndex;
    static cookiesCreated = 0;
    constructor(options){
        if (options) {
            this.name = options.name;
            this.value = options.value;
            this.path = options.path;
            this.domain = options.domain;
            this.expires = options.expires;
            this.maxAge = options.maxAge;
            this.secure = options.secure;
            this.httpOnly = options.httpOnly;
            this.sameSite = options.sameSite;
            if (options.creationDate) {
                this.creationDate = options.creationDate;
            }
        }
        Object.defineProperty(this, "creationIndex", {
            configurable: false,
            enumerable: false,
            writable: true,
            value: ++Cookie.cookiesCreated
        });
    }
    static from(cookieStr) {
        const options = {
            name: undefined,
            value: undefined,
            path: undefined,
            domain: undefined,
            expires: undefined,
            maxAge: undefined,
            secure: undefined,
            httpOnly: undefined,
            sameSite: undefined,
            creationDate: Date.now()
        };
        const unparsed = cookieStr.slice().trim();
        const attrAndValueList = unparsed.split(";");
        const keyValuePairString = trimTerminator(attrAndValueList.shift() || "").trim();
        const keyValuePairEqualsIndex = keyValuePairString.indexOf("=");
        if (keyValuePairEqualsIndex < 0) {
            return new Cookie();
        }
        const name = keyValuePairString.slice(0, keyValuePairEqualsIndex);
        const value = trimWrappingDoubleQuotes(keyValuePairString.slice(keyValuePairEqualsIndex + 1));
        if (!(isValidName(name) && isValidValue(value))) {
            return new Cookie();
        }
        options.name = name;
        options.value = value;
        while(attrAndValueList.length){
            const cookieAV = attrAndValueList.shift()?.trim();
            if (!cookieAV) {
                continue;
            }
            const avSeperatorIndex = cookieAV.indexOf("=");
            let attrKey, attrValue;
            if (avSeperatorIndex === -1) {
                attrKey = cookieAV;
                attrValue = "";
            } else {
                attrKey = cookieAV.substr(0, avSeperatorIndex);
                attrValue = cookieAV.substr(avSeperatorIndex + 1);
            }
            attrKey = attrKey.trim().toLowerCase();
            if (attrValue) {
                attrValue = attrValue.trim();
            }
            switch(attrKey){
                case "expires":
                    if (attrValue) {
                        const expires = new Date(attrValue).getTime();
                        if (expires && !isNaN(expires)) {
                            options.expires = expires;
                        }
                    }
                    break;
                case "max-age":
                    if (attrValue) {
                        const maxAge = parseInt(attrValue, 10);
                        if (!isNaN(maxAge)) {
                            options.maxAge = maxAge;
                        }
                    }
                    break;
                case "domain":
                    if (attrValue) {
                        const domain = parseURL(attrValue).host;
                        if (domain) {
                            options.domain = domain;
                        }
                    }
                    break;
                case "path":
                    if (attrValue) {
                        options.path = attrValue.startsWith("/") ? attrValue : "/" + attrValue;
                    }
                    break;
                case "secure":
                    options.secure = true;
                    break;
                case "httponly":
                    options.httpOnly = true;
                    break;
                case "samesite":
                    {
                        const lowerCasedSameSite = attrValue.toLowerCase();
                        switch(lowerCasedSameSite){
                            case "strict":
                                options.sameSite = "Strict";
                                break;
                            case "lax":
                                options.sameSite = "Lax";
                                break;
                            case "none":
                                options.sameSite = "None";
                                break;
                            default:
                                break;
                        }
                        break;
                    }
                default:
                    break;
            }
        }
        return new Cookie(options);
    }
    isValid() {
        return isValidName(this.name) && isValidValue(this.value);
    }
    canSendTo(url) {
        const urlObj = parseURL(url);
        if (this.secure && urlObj.protocol !== "https:") {
            return false;
        }
        if (this.sameSite === "None" && !this.secure) return false;
        if (this.path) {
            if (this.path === urlObj.pathname) {
                return true;
            }
            if (urlObj.pathname.startsWith(this.path) && this.path[this.path.length - 1] === "/") {
                return true;
            }
            if (this.path.length < urlObj.pathname.length && urlObj.pathname.startsWith(this.path) && urlObj.pathname[this.path.length] === "/") {
                return true;
            }
            return false;
        }
        if (this.domain) {
            const host = urlObj.host;
            if (isSameDomainOrSubdomain(this.domain, host)) {
                return true;
            }
        }
        return false;
    }
    getCookieString() {
        return `${this.name || ""}=${this.value || ""}`;
    }
    setDomain(url) {
        this.domain = parseURL(url).host;
    }
    setPath(url) {
        const uriPath = parseURL(url).pathname;
        if (!uriPath || uriPath[0] !== "/") {
            this.path = "/";
        } else {
            const rightmostSlashIdx = uriPath.lastIndexOf("/");
            if (rightmostSlashIdx <= 0) {
                this.path = "/";
            } else {
                this.path = uriPath.slice(0, rightmostSlashIdx);
            }
        }
    }
    setExpires(exp) {
        if (exp instanceof Date) {
            this.expires = exp.getTime();
        } else if (typeof exp === "number" && exp >= 0) {
            this.expires = exp;
        }
    }
    isExpired() {
        if (this.maxAge !== undefined) {
            if (Date.now() - this.creationDate >= this.maxAge * 1000) {
                return true;
            }
        }
        if (this.expires !== undefined) {
            if (Date.now() - this.expires >= 0) {
                return true;
            }
        }
        return false;
    }
    toString() {
        let str = this.getCookieString();
        if (this.expires && this.expires !== Infinity) {
            str += "; Expires=" + new Date(this.expires).toUTCString();
        }
        if (this.maxAge && this.maxAge !== Infinity) {
            str += `; Max-Age=${this.maxAge}`;
        }
        if (this.domain) {
            str += `; Domain=${this.domain}`;
        }
        if (this.path) {
            str += `; Path=${this.path}`;
        }
        if (this.secure) {
            str += "; Secure";
        }
        if (this.httpOnly) {
            str += "; HttpOnly";
        }
        if (this.sameSite) {
            str += `; SameSite=${this.sameSite}`;
        }
        return str;
    }
    clone() {
        return new Cookie(JSON.parse(JSON.stringify(this)));
    }
}
const strictMatchProps = [
    "value",
    "secure",
    "httpOnly",
    "maxAge",
    "expires",
    "sameSite"
];
function cookieMatches(options, comparedWith, strictMatch = false) {
    if (options.path !== undefined && !comparedWith.path?.startsWith(options.path)) {
        return false;
    }
    if (options.domain) {
        if (!isSameDomainOrSubdomain(options.domain, comparedWith.domain)) {
            return false;
        }
    }
    if (options.name !== undefined && options.name !== comparedWith.name) {
        return false;
    }
    if (strictMatch && strictMatchProps.some((propKey)=>options[propKey] !== undefined && options[propKey] !== comparedWith[propKey])) {
        return false;
    }
    return true;
}
function cookieCompare(a, b) {
    let cmp = 0;
    const aPathLen = a.path?.length || 0;
    const bPathLen = b.path?.length || 0;
    cmp = bPathLen - aPathLen;
    if (cmp !== 0) {
        return cmp;
    }
    const aTime = a.creationDate || 2147483647000;
    const bTime = b.creationDate || 2147483647000;
    cmp = aTime - bTime;
    if (cmp !== 0) {
        return cmp;
    }
    cmp = a.creationIndex - b.creationIndex;
    return cmp;
}
class CookieJar {
    cookies = Array();
    constructor(cookies){
        this.replaceCookies(cookies);
    }
    setCookie(cookie, url) {
        let cookieObj;
        if (typeof cookie === "string") {
            cookieObj = Cookie.from(cookie);
        } else {
            cookieObj = cookie;
        }
        if (url) {
            if (!cookieObj.domain) {
                cookieObj.setDomain(url);
            }
            if (!cookieObj.path) {
                cookieObj.setPath(url);
            }
        }
        if (!cookieObj.isValid()) {
            return;
        }
        const foundCookie = this.getCookie(cookieObj);
        if (foundCookie) {
            const indexOfCookie = this.cookies.indexOf(foundCookie);
            if (!cookieObj.isExpired()) {
                this.cookies.splice(indexOfCookie, 1, cookieObj);
            } else {
                this.cookies.splice(indexOfCookie, 1);
            }
        } else if (!cookieObj.isExpired()) {
            this.cookies.push(cookieObj);
        }
        this.cookies.sort(cookieCompare);
    }
    getCookie(options) {
        const strictMatch = typeof options.isValid !== "function";
        for (const [index, cookie] of this.cookies.entries()){
            if (cookieMatches(options, cookie, strictMatch)) {
                if (!cookie.isExpired()) {
                    return cookie;
                } else {
                    this.cookies.splice(index, 1);
                    return undefined;
                }
            }
        }
    }
    getCookies(options) {
        if (options) {
            const matchedCookies = [];
            const removeCookies = [];
            for (const cookie of this.cookies){
                if (cookieMatches(options, cookie)) {
                    if (!cookie.isExpired()) {
                        matchedCookies.push(cookie);
                    } else {
                        removeCookies.push(cookie);
                    }
                }
            }
            if (removeCookies.length) {
                this.cookies = this.cookies.filter((cookie)=>!removeCookies.includes(cookie));
            }
            return matchedCookies;
        } else {
            return this.cookies;
        }
    }
    getCookieString(url) {
        const searchCookie = new Cookie();
        searchCookie.setDomain(url);
        const cookiesToSend = this.getCookies(searchCookie).filter((cookie)=>{
            return cookie.canSendTo(parseURL(url));
        }).map((c)=>c.getCookieString()).join("; ");
        return cookiesToSend;
    }
    toJSON() {
        return this.cookies;
    }
    removeCookie(options) {
        for (const [index, cookie] of this.cookies.entries()){
            if (cookieMatches(options, cookie)) {
                return this.cookies.splice(index, 1)[0];
            }
        }
    }
    removeCookies(options) {
        if (options) {
            const deletedCookies = [];
            this.cookies = this.cookies.filter((cookie)=>{
                if (cookieMatches(options, cookie)) {
                    deletedCookies.push(cookie);
                    return false;
                }
                return true;
            });
            return deletedCookies.length ? deletedCookies : undefined;
        } else {
            this.cookies = [];
        }
    }
    replaceCookies(cookies) {
        if (cookies?.length) {
            if (typeof cookies[0].isValid === "function") {
                this.cookies = cookies;
            } else {
                this.cookies = [];
                for (const option of cookies){
                    this.cookies.push(new Cookie(option));
                }
            }
        } else {
            this.cookies = [];
        }
    }
}
function wrapFetch(options) {
    const { cookieJar =new CookieJar() , fetch =globalThis.fetch  } = options || {};
    async function wrappedFetch(input, init) {
        if (!input) {
            return await fetch(input);
        }
        const cookieString = cookieJar.getCookieString(input);
        let interceptedInit;
        if (init) {
            interceptedInit = init;
        } else if (input instanceof Request) {
            interceptedInit = input;
        } else {
            interceptedInit = {};
        }
        if (!(interceptedInit.headers instanceof Headers)) {
            interceptedInit.headers = new Headers(interceptedInit.headers || {});
        }
        interceptedInit.headers.set("cookie", cookieString);
        const response = await fetch(input, interceptedInit);
        response.headers.forEach((value, key)=>{
            if (key.toLowerCase() === "set-cookie") {
                cookieJar.setCookie(value, input);
            }
        });
        return response;
    }
    return wrappedFetch;
}
const base64abc = [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "M",
    "N",
    "O",
    "P",
    "Q",
    "R",
    "S",
    "T",
    "U",
    "V",
    "W",
    "X",
    "Y",
    "Z",
    "a",
    "b",
    "c",
    "d",
    "e",
    "f",
    "g",
    "h",
    "i",
    "j",
    "k",
    "l",
    "m",
    "n",
    "o",
    "p",
    "q",
    "r",
    "s",
    "t",
    "u",
    "v",
    "w",
    "x",
    "y",
    "z",
    "0",
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "8",
    "9",
    "+",
    "/"
];
function encode(data) {
    const uint8 = typeof data === "string" ? new TextEncoder().encode(data) : data instanceof Uint8Array ? data : new Uint8Array(data);
    let result = "", i;
    const l = uint8.length;
    for(i = 2; i < l; i += 3){
        result += base64abc[uint8[i - 2] >> 2];
        result += base64abc[(uint8[i - 2] & 0x03) << 4 | uint8[i - 1] >> 4];
        result += base64abc[(uint8[i - 1] & 0x0f) << 2 | uint8[i] >> 6];
        result += base64abc[uint8[i] & 0x3f];
    }
    if (i === l + 1) {
        result += base64abc[uint8[i - 2] >> 2];
        result += base64abc[(uint8[i - 2] & 0x03) << 4];
        result += "==";
    }
    if (i === l) {
        result += base64abc[uint8[i - 2] >> 2];
        result += base64abc[(uint8[i - 2] & 0x03) << 4 | uint8[i - 1] >> 4];
        result += base64abc[(uint8[i - 1] & 0x0f) << 2];
        result += "=";
    }
    return result;
}
function decode(b64) {
    const binString = atob(b64);
    const size = binString.length;
    const bytes = new Uint8Array(size);
    for(let i = 0; i < size; i++){
        bytes[i] = binString.charCodeAt(i);
    }
    return bytes;
}
const mod = {
    encode: encode,
    decode: decode
};
class DenoStdInternalError extends Error {
    constructor(message){
        super(message);
        this.name = "DenoStdInternalError";
    }
}
function assert(expr, msg = "") {
    if (!expr) {
        throw new DenoStdInternalError(msg);
    }
}
const { hasOwn  } = Object;
function get(obj, key) {
    if (hasOwn(obj, key)) {
        return obj[key];
    }
}
function getForce(obj, key) {
    const v = get(obj, key);
    assert(v != null);
    return v;
}
function isNumber(x) {
    if (typeof x === "number") return true;
    if (/^0x[0-9a-f]+$/i.test(String(x))) return true;
    return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(String(x));
}
function hasKey(obj, keys) {
    let o = obj;
    keys.slice(0, -1).forEach((key)=>{
        o = get(o, key) ?? {};
    });
    const key = keys[keys.length - 1];
    return hasOwn(o, key);
}
function parse(args, { "--": doubleDash = false , alias ={} , boolean: __boolean = false , default: defaults = {} , stopEarly =false , string =[] , collect =[] , negatable =[] , unknown =(i)=>i  } = {}) {
    const flags = {
        bools: {},
        strings: {},
        unknownFn: unknown,
        allBools: false,
        collect: {},
        negatable: {}
    };
    if (__boolean !== undefined) {
        if (typeof __boolean === "boolean") {
            flags.allBools = !!__boolean;
        } else {
            const booleanArgs = typeof __boolean === "string" ? [
                __boolean
            ] : __boolean;
            for (const key of booleanArgs.filter(Boolean)){
                flags.bools[key] = true;
            }
        }
    }
    const aliases = {};
    if (alias !== undefined) {
        for(const key in alias){
            const val = getForce(alias, key);
            if (typeof val === "string") {
                aliases[key] = [
                    val
                ];
            } else {
                aliases[key] = val;
            }
            for (const alias of getForce(aliases, key)){
                aliases[alias] = [
                    key
                ].concat(aliases[key].filter((y)=>alias !== y));
            }
        }
    }
    if (string !== undefined) {
        const stringArgs = typeof string === "string" ? [
            string
        ] : string;
        for (const key of stringArgs.filter(Boolean)){
            flags.strings[key] = true;
            const alias = get(aliases, key);
            if (alias) {
                for (const al of alias){
                    flags.strings[al] = true;
                }
            }
        }
    }
    if (collect !== undefined) {
        const collectArgs = typeof collect === "string" ? [
            collect
        ] : collect;
        for (const key of collectArgs.filter(Boolean)){
            flags.collect[key] = true;
            const alias = get(aliases, key);
            if (alias) {
                for (const al of alias){
                    flags.collect[al] = true;
                }
            }
        }
    }
    if (negatable !== undefined) {
        const negatableArgs = typeof negatable === "string" ? [
            negatable
        ] : negatable;
        for (const key of negatableArgs.filter(Boolean)){
            flags.negatable[key] = true;
            const alias = get(aliases, key);
            if (alias) {
                for (const al of alias){
                    flags.negatable[al] = true;
                }
            }
        }
    }
    const argv = {
        _: []
    };
    function argDefined(key, arg) {
        return flags.allBools && /^--[^=]+$/.test(arg) || get(flags.bools, key) || !!get(flags.strings, key) || !!get(aliases, key);
    }
    function setKey(obj, name, value, collect = true) {
        let o = obj;
        const keys = name.split(".");
        keys.slice(0, -1).forEach(function(key) {
            if (get(o, key) === undefined) {
                o[key] = {};
            }
            o = get(o, key);
        });
        const key = keys[keys.length - 1];
        const collectable = collect && !!get(flags.collect, name);
        if (!collectable) {
            o[key] = value;
        } else if (get(o, key) === undefined) {
            o[key] = [
                value
            ];
        } else if (Array.isArray(get(o, key))) {
            o[key].push(value);
        } else {
            o[key] = [
                get(o, key),
                value
            ];
        }
    }
    function setArg(key, val, arg = undefined, collect) {
        if (arg && flags.unknownFn && !argDefined(key, arg)) {
            if (flags.unknownFn(arg, key, val) === false) return;
        }
        const value = !get(flags.strings, key) && isNumber(val) ? Number(val) : val;
        setKey(argv, key, value, collect);
        const alias = get(aliases, key);
        if (alias) {
            for (const x of alias){
                setKey(argv, x, value, collect);
            }
        }
    }
    function aliasIsBoolean(key) {
        return getForce(aliases, key).some((x)=>typeof get(flags.bools, x) === "boolean");
    }
    let notFlags = [];
    if (args.includes("--")) {
        notFlags = args.slice(args.indexOf("--") + 1);
        args = args.slice(0, args.indexOf("--"));
    }
    for(let i = 0; i < args.length; i++){
        const arg = args[i];
        if (/^--.+=/.test(arg)) {
            const m = arg.match(/^--([^=]+)=(.*)$/s);
            assert(m != null);
            const [, key, value] = m;
            if (flags.bools[key]) {
                const booleanValue = value !== "false";
                setArg(key, booleanValue, arg);
            } else {
                setArg(key, value, arg);
            }
        } else if (/^--no-.+/.test(arg) && get(flags.negatable, arg.replace(/^--no-/, ""))) {
            const m = arg.match(/^--no-(.+)/);
            assert(m != null);
            setArg(m[1], false, arg, false);
        } else if (/^--.+/.test(arg)) {
            const m = arg.match(/^--(.+)/);
            assert(m != null);
            const [, key] = m;
            const next = args[i + 1];
            if (next !== undefined && !/^-/.test(next) && !get(flags.bools, key) && !flags.allBools && (get(aliases, key) ? !aliasIsBoolean(key) : true)) {
                setArg(key, next, arg);
                i++;
            } else if (/^(true|false)$/.test(next)) {
                setArg(key, next === "true", arg);
                i++;
            } else {
                setArg(key, get(flags.strings, key) ? "" : true, arg);
            }
        } else if (/^-[^-]+/.test(arg)) {
            const letters = arg.slice(1, -1).split("");
            let broken = false;
            for(let j = 0; j < letters.length; j++){
                const next = arg.slice(j + 2);
                if (next === "-") {
                    setArg(letters[j], next, arg);
                    continue;
                }
                if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) {
                    setArg(letters[j], next.split(/=(.+)/)[1], arg);
                    broken = true;
                    break;
                }
                if (/[A-Za-z]/.test(letters[j]) && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
                    setArg(letters[j], next, arg);
                    broken = true;
                    break;
                }
                if (letters[j + 1] && letters[j + 1].match(/\W/)) {
                    setArg(letters[j], arg.slice(j + 2), arg);
                    broken = true;
                    break;
                } else {
                    setArg(letters[j], get(flags.strings, letters[j]) ? "" : true, arg);
                }
            }
            const [key] = arg.slice(-1);
            if (!broken && key !== "-") {
                if (args[i + 1] && !/^(-|--)[^-]/.test(args[i + 1]) && !get(flags.bools, key) && (get(aliases, key) ? !aliasIsBoolean(key) : true)) {
                    setArg(key, args[i + 1], arg);
                    i++;
                } else if (args[i + 1] && /^(true|false)$/.test(args[i + 1])) {
                    setArg(key, args[i + 1] === "true", arg);
                    i++;
                } else {
                    setArg(key, get(flags.strings, key) ? "" : true, arg);
                }
            }
        } else {
            if (!flags.unknownFn || flags.unknownFn(arg) !== false) {
                argv._.push(flags.strings["_"] ?? !isNumber(arg) ? arg : Number(arg));
            }
            if (stopEarly) {
                argv._.push(...args.slice(i + 1));
                break;
            }
        }
    }
    for (const [key, value] of Object.entries(defaults)){
        if (!hasKey(argv, key.split("."))) {
            setKey(argv, key, value);
            if (aliases[key]) {
                for (const x of aliases[key]){
                    setKey(argv, x, value);
                }
            }
        }
    }
    for (const key of Object.keys(flags.bools)){
        if (!hasKey(argv, key.split("."))) {
            const value = get(flags.collect, key) ? [] : false;
            setKey(argv, key, value, false);
        }
    }
    for (const key of Object.keys(flags.strings)){
        if (!hasKey(argv, key.split(".")) && get(flags.collect, key)) {
            setKey(argv, key, [], false);
        }
    }
    if (doubleDash) {
        argv["--"] = [];
        for (const key of notFlags){
            argv["--"].push(key);
        }
    } else {
        for (const key of notFlags){
            argv._.push(key);
        }
    }
    return argv;
}
const mod1 = {
    parse: parse
};
class BytesList {
    #len = 0;
    #chunks = [];
    constructor(){}
    size() {
        return this.#len;
    }
    add(value, start = 0, end = value.byteLength) {
        if (value.byteLength === 0 || end - start === 0) {
            return;
        }
        checkRange(start, end, value.byteLength);
        this.#chunks.push({
            value,
            end,
            start,
            offset: this.#len
        });
        this.#len += end - start;
    }
    shift(n) {
        if (n === 0) {
            return;
        }
        if (this.#len <= n) {
            this.#chunks = [];
            this.#len = 0;
            return;
        }
        const idx = this.getChunkIndex(n);
        this.#chunks.splice(0, idx);
        const [chunk] = this.#chunks;
        if (chunk) {
            const diff = n - chunk.offset;
            chunk.start += diff;
        }
        let offset = 0;
        for (const chunk of this.#chunks){
            chunk.offset = offset;
            offset += chunk.end - chunk.start;
        }
        this.#len = offset;
    }
    getChunkIndex(pos) {
        let max = this.#chunks.length;
        let min = 0;
        while(true){
            const i = min + Math.floor((max - min) / 2);
            if (i < 0 || this.#chunks.length <= i) {
                return -1;
            }
            const { offset , start , end  } = this.#chunks[i];
            const len = end - start;
            if (offset <= pos && pos < offset + len) {
                return i;
            } else if (offset + len <= pos) {
                min = i + 1;
            } else {
                max = i - 1;
            }
        }
    }
    get(i) {
        if (i < 0 || this.#len <= i) {
            throw new Error("out of range");
        }
        const idx = this.getChunkIndex(i);
        const { value , offset , start  } = this.#chunks[idx];
        return value[start + i - offset];
    }
    *iterator(start = 0) {
        const startIdx = this.getChunkIndex(start);
        if (startIdx < 0) return;
        const first = this.#chunks[startIdx];
        let firstOffset = start - first.offset;
        for(let i = startIdx; i < this.#chunks.length; i++){
            const chunk = this.#chunks[i];
            for(let j = chunk.start + firstOffset; j < chunk.end; j++){
                yield chunk.value[j];
            }
            firstOffset = 0;
        }
    }
    slice(start, end = this.#len) {
        if (end === start) {
            return new Uint8Array();
        }
        checkRange(start, end, this.#len);
        const result = new Uint8Array(end - start);
        const startIdx = this.getChunkIndex(start);
        const endIdx = this.getChunkIndex(end - 1);
        let written = 0;
        for(let i = startIdx; i < endIdx; i++){
            const chunk = this.#chunks[i];
            const len = chunk.end - chunk.start;
            result.set(chunk.value.subarray(chunk.start, chunk.end), written);
            written += len;
        }
        const last = this.#chunks[endIdx];
        const rest = end - start - written;
        result.set(last.value.subarray(last.start, last.start + rest), written);
        return result;
    }
    concat() {
        const result = new Uint8Array(this.#len);
        let sum = 0;
        for (const { value , start , end  } of this.#chunks){
            result.set(value.subarray(start, end), sum);
            sum += end - start;
        }
        return result;
    }
}
function checkRange(start, end, len) {
    if (start < 0 || len < start || end < 0 || len < end || end < start) {
        throw new Error("invalid range");
    }
}
function concat(...buf) {
    let length = 0;
    for (const b of buf){
        length += b.length;
    }
    const output = new Uint8Array(length);
    let index = 0;
    for (const b of buf){
        output.set(b, index);
        index += b.length;
    }
    return output;
}
function copy(src, dst, off = 0) {
    off = Math.max(0, Math.min(off, dst.byteLength));
    const dstBytesAvailable = dst.byteLength - off;
    if (src.byteLength > dstBytesAvailable) {
        src = src.subarray(0, dstBytesAvailable);
    }
    dst.set(src, off);
    return src.byteLength;
}
const MIN_READ = 32 * 1024;
const MAX_SIZE = 2 ** 32 - 2;
class Buffer {
    #buf;
    #off = 0;
    constructor(ab){
        this.#buf = ab === undefined ? new Uint8Array(0) : new Uint8Array(ab);
    }
    bytes(options = {
        copy: true
    }) {
        if (options.copy === false) return this.#buf.subarray(this.#off);
        return this.#buf.slice(this.#off);
    }
    empty() {
        return this.#buf.byteLength <= this.#off;
    }
    get length() {
        return this.#buf.byteLength - this.#off;
    }
    get capacity() {
        return this.#buf.buffer.byteLength;
    }
    truncate(n) {
        if (n === 0) {
            this.reset();
            return;
        }
        if (n < 0 || n > this.length) {
            throw Error("bytes.Buffer: truncation out of range");
        }
        this.#reslice(this.#off + n);
    }
    reset() {
        this.#reslice(0);
        this.#off = 0;
    }
    #tryGrowByReslice(n) {
        const l = this.#buf.byteLength;
        if (n <= this.capacity - l) {
            this.#reslice(l + n);
            return l;
        }
        return -1;
    }
    #reslice(len) {
        assert(len <= this.#buf.buffer.byteLength);
        this.#buf = new Uint8Array(this.#buf.buffer, 0, len);
    }
    readSync(p) {
        if (this.empty()) {
            this.reset();
            if (p.byteLength === 0) {
                return 0;
            }
            return null;
        }
        const nread = copy(this.#buf.subarray(this.#off), p);
        this.#off += nread;
        return nread;
    }
    read(p) {
        const rr = this.readSync(p);
        return Promise.resolve(rr);
    }
    writeSync(p) {
        const m = this.#grow(p.byteLength);
        return copy(p, this.#buf, m);
    }
    write(p) {
        const n = this.writeSync(p);
        return Promise.resolve(n);
    }
    #grow(n1) {
        const m = this.length;
        if (m === 0 && this.#off !== 0) {
            this.reset();
        }
        const i = this.#tryGrowByReslice(n1);
        if (i >= 0) {
            return i;
        }
        const c = this.capacity;
        if (n1 <= Math.floor(c / 2) - m) {
            copy(this.#buf.subarray(this.#off), this.#buf);
        } else if (c + n1 > MAX_SIZE) {
            throw new Error("The buffer cannot be grown beyond the maximum size.");
        } else {
            const buf = new Uint8Array(Math.min(2 * c + n1, MAX_SIZE));
            copy(this.#buf.subarray(this.#off), buf);
            this.#buf = buf;
        }
        this.#off = 0;
        this.#reslice(Math.min(m + n1, MAX_SIZE));
        return m;
    }
    grow(n) {
        if (n < 0) {
            throw Error("Buffer.grow: negative count");
        }
        const m = this.#grow(n);
        this.#reslice(m);
    }
    async readFrom(r) {
        let n = 0;
        const tmp = new Uint8Array(MIN_READ);
        while(true){
            const shouldGrow = this.capacity - this.length < MIN_READ;
            const buf = shouldGrow ? tmp : new Uint8Array(this.#buf.buffer, this.length);
            const nread = await r.read(buf);
            if (nread === null) {
                return n;
            }
            if (shouldGrow) this.writeSync(buf.subarray(0, nread));
            else this.#reslice(this.length + nread);
            n += nread;
        }
    }
    readFromSync(r) {
        let n = 0;
        const tmp = new Uint8Array(MIN_READ);
        while(true){
            const shouldGrow = this.capacity - this.length < MIN_READ;
            const buf = shouldGrow ? tmp : new Uint8Array(this.#buf.buffer, this.length);
            const nread = r.readSync(buf);
            if (nread === null) {
                return n;
            }
            if (shouldGrow) this.writeSync(buf.subarray(0, nread));
            else this.#reslice(this.length + nread);
            n += nread;
        }
    }
}
const MIN_BUF_SIZE = 16;
const CR = "\r".charCodeAt(0);
const LF = "\n".charCodeAt(0);
class BufferFullError extends Error {
    partial;
    name;
    constructor(partial){
        super("Buffer full");
        this.partial = partial;
        this.name = "BufferFullError";
    }
}
class PartialReadError extends Error {
    name = "PartialReadError";
    partial;
    constructor(){
        super("Encountered UnexpectedEof, data only partially read");
    }
}
class BufReader {
    #buf;
    #rd;
    #r = 0;
    #w = 0;
    #eof = false;
    static create(r, size = 4096) {
        return r instanceof BufReader ? r : new BufReader(r, size);
    }
    constructor(rd, size = 4096){
        if (size < 16) {
            size = MIN_BUF_SIZE;
        }
        this.#reset(new Uint8Array(size), rd);
    }
    size() {
        return this.#buf.byteLength;
    }
    buffered() {
        return this.#w - this.#r;
    }
    #fill = async ()=>{
        if (this.#r > 0) {
            this.#buf.copyWithin(0, this.#r, this.#w);
            this.#w -= this.#r;
            this.#r = 0;
        }
        if (this.#w >= this.#buf.byteLength) {
            throw Error("bufio: tried to fill full buffer");
        }
        for(let i = 100; i > 0; i--){
            const rr = await this.#rd.read(this.#buf.subarray(this.#w));
            if (rr === null) {
                this.#eof = true;
                return;
            }
            assert(rr >= 0, "negative read");
            this.#w += rr;
            if (rr > 0) {
                return;
            }
        }
        throw new Error(`No progress after ${100} read() calls`);
    };
    reset(r) {
        this.#reset(this.#buf, r);
    }
    #reset = (buf, rd)=>{
        this.#buf = buf;
        this.#rd = rd;
        this.#eof = false;
    };
    async read(p) {
        let rr = p.byteLength;
        if (p.byteLength === 0) return rr;
        if (this.#r === this.#w) {
            if (p.byteLength >= this.#buf.byteLength) {
                const rr = await this.#rd.read(p);
                const nread = rr ?? 0;
                assert(nread >= 0, "negative read");
                return rr;
            }
            this.#r = 0;
            this.#w = 0;
            rr = await this.#rd.read(this.#buf);
            if (rr === 0 || rr === null) return rr;
            assert(rr >= 0, "negative read");
            this.#w += rr;
        }
        const copied = copy(this.#buf.subarray(this.#r, this.#w), p, 0);
        this.#r += copied;
        return copied;
    }
    async readFull(p) {
        let bytesRead = 0;
        while(bytesRead < p.length){
            try {
                const rr = await this.read(p.subarray(bytesRead));
                if (rr === null) {
                    if (bytesRead === 0) {
                        return null;
                    } else {
                        throw new PartialReadError();
                    }
                }
                bytesRead += rr;
            } catch (err) {
                if (err instanceof PartialReadError) {
                    err.partial = p.subarray(0, bytesRead);
                } else if (err instanceof Error) {
                    const e = new PartialReadError();
                    e.partial = p.subarray(0, bytesRead);
                    e.stack = err.stack;
                    e.message = err.message;
                    e.cause = err.cause;
                    throw err;
                }
                throw err;
            }
        }
        return p;
    }
    async readByte() {
        while(this.#r === this.#w){
            if (this.#eof) return null;
            await this.#fill();
        }
        const c = this.#buf[this.#r];
        this.#r++;
        return c;
    }
    async readString(delim) {
        if (delim.length !== 1) {
            throw new Error("Delimiter should be a single character");
        }
        const buffer = await this.readSlice(delim.charCodeAt(0));
        if (buffer === null) return null;
        return new TextDecoder().decode(buffer);
    }
    async readLine() {
        let line = null;
        try {
            line = await this.readSlice(LF);
        } catch (err) {
            let partial;
            if (err instanceof PartialReadError) {
                partial = err.partial;
                assert(partial instanceof Uint8Array, "bufio: caught error from `readSlice()` without `partial` property");
            }
            if (!(err instanceof BufferFullError)) {
                throw err;
            }
            partial = err.partial;
            if (!this.#eof && partial && partial.byteLength > 0 && partial[partial.byteLength - 1] === CR) {
                assert(this.#r > 0, "bufio: tried to rewind past start of buffer");
                this.#r--;
                partial = partial.subarray(0, partial.byteLength - 1);
            }
            if (partial) {
                return {
                    line: partial,
                    more: !this.#eof
                };
            }
        }
        if (line === null) {
            return null;
        }
        if (line.byteLength === 0) {
            return {
                line,
                more: false
            };
        }
        if (line[line.byteLength - 1] == LF) {
            let drop = 1;
            if (line.byteLength > 1 && line[line.byteLength - 2] === CR) {
                drop = 2;
            }
            line = line.subarray(0, line.byteLength - drop);
        }
        return {
            line,
            more: false
        };
    }
    async readSlice(delim) {
        let s = 0;
        let slice;
        while(true){
            let i = this.#buf.subarray(this.#r + s, this.#w).indexOf(delim);
            if (i >= 0) {
                i += s;
                slice = this.#buf.subarray(this.#r, this.#r + i + 1);
                this.#r += i + 1;
                break;
            }
            if (this.#eof) {
                if (this.#r === this.#w) {
                    return null;
                }
                slice = this.#buf.subarray(this.#r, this.#w);
                this.#r = this.#w;
                break;
            }
            if (this.buffered() >= this.#buf.byteLength) {
                this.#r = this.#w;
                const oldbuf = this.#buf;
                const newbuf = this.#buf.slice(0);
                this.#buf = newbuf;
                throw new BufferFullError(oldbuf);
            }
            s = this.#w - this.#r;
            try {
                await this.#fill();
            } catch (err) {
                if (err instanceof PartialReadError) {
                    err.partial = slice;
                } else if (err instanceof Error) {
                    const e = new PartialReadError();
                    e.partial = slice;
                    e.stack = err.stack;
                    e.message = err.message;
                    e.cause = err.cause;
                    throw err;
                }
                throw err;
            }
        }
        return slice;
    }
    async peek(n) {
        if (n < 0) {
            throw Error("negative count");
        }
        let avail = this.#w - this.#r;
        while(avail < n && avail < this.#buf.byteLength && !this.#eof){
            try {
                await this.#fill();
            } catch (err) {
                if (err instanceof PartialReadError) {
                    err.partial = this.#buf.subarray(this.#r, this.#w);
                } else if (err instanceof Error) {
                    const e = new PartialReadError();
                    e.partial = this.#buf.subarray(this.#r, this.#w);
                    e.stack = err.stack;
                    e.message = err.message;
                    e.cause = err.cause;
                    throw err;
                }
                throw err;
            }
            avail = this.#w - this.#r;
        }
        if (avail === 0 && this.#eof) {
            return null;
        } else if (avail < n && this.#eof) {
            return this.#buf.subarray(this.#r, this.#r + avail);
        } else if (avail < n) {
            throw new BufferFullError(this.#buf.subarray(this.#r, this.#w));
        }
        return this.#buf.subarray(this.#r, this.#r + n);
    }
}
class AbstractBufBase {
    buf;
    usedBufferBytes = 0;
    err = null;
    constructor(buf){
        this.buf = buf;
    }
    size() {
        return this.buf.byteLength;
    }
    available() {
        return this.buf.byteLength - this.usedBufferBytes;
    }
    buffered() {
        return this.usedBufferBytes;
    }
}
class BufWriter extends AbstractBufBase {
    #writer;
    static create(writer, size = 4096) {
        return writer instanceof BufWriter ? writer : new BufWriter(writer, size);
    }
    constructor(writer, size = 4096){
        super(new Uint8Array(size <= 0 ? 4096 : size));
        this.#writer = writer;
    }
    reset(w) {
        this.err = null;
        this.usedBufferBytes = 0;
        this.#writer = w;
    }
    async flush() {
        if (this.err !== null) throw this.err;
        if (this.usedBufferBytes === 0) return;
        try {
            const p = this.buf.subarray(0, this.usedBufferBytes);
            let nwritten = 0;
            while(nwritten < p.length){
                nwritten += await this.#writer.write(p.subarray(nwritten));
            }
        } catch (e) {
            if (e instanceof Error) {
                this.err = e;
            }
            throw e;
        }
        this.buf = new Uint8Array(this.buf.length);
        this.usedBufferBytes = 0;
    }
    async write(data) {
        if (this.err !== null) throw this.err;
        if (data.length === 0) return 0;
        let totalBytesWritten = 0;
        let numBytesWritten = 0;
        while(data.byteLength > this.available()){
            if (this.buffered() === 0) {
                try {
                    numBytesWritten = await this.#writer.write(data);
                } catch (e) {
                    if (e instanceof Error) {
                        this.err = e;
                    }
                    throw e;
                }
            } else {
                numBytesWritten = copy(data, this.buf, this.usedBufferBytes);
                this.usedBufferBytes += numBytesWritten;
                await this.flush();
            }
            totalBytesWritten += numBytesWritten;
            data = data.subarray(numBytesWritten);
        }
        numBytesWritten = copy(data, this.buf, this.usedBufferBytes);
        this.usedBufferBytes += numBytesWritten;
        totalBytesWritten += numBytesWritten;
        return totalBytesWritten;
    }
}
class BufWriterSync extends AbstractBufBase {
    #writer;
    static create(writer, size = 4096) {
        return writer instanceof BufWriterSync ? writer : new BufWriterSync(writer, size);
    }
    constructor(writer, size = 4096){
        super(new Uint8Array(size <= 0 ? 4096 : size));
        this.#writer = writer;
    }
    reset(w) {
        this.err = null;
        this.usedBufferBytes = 0;
        this.#writer = w;
    }
    flush() {
        if (this.err !== null) throw this.err;
        if (this.usedBufferBytes === 0) return;
        try {
            const p = this.buf.subarray(0, this.usedBufferBytes);
            let nwritten = 0;
            while(nwritten < p.length){
                nwritten += this.#writer.writeSync(p.subarray(nwritten));
            }
        } catch (e) {
            if (e instanceof Error) {
                this.err = e;
            }
            throw e;
        }
        this.buf = new Uint8Array(this.buf.length);
        this.usedBufferBytes = 0;
    }
    writeSync(data) {
        if (this.err !== null) throw this.err;
        if (data.length === 0) return 0;
        let totalBytesWritten = 0;
        let numBytesWritten = 0;
        while(data.byteLength > this.available()){
            if (this.buffered() === 0) {
                try {
                    numBytesWritten = this.#writer.writeSync(data);
                } catch (e) {
                    if (e instanceof Error) {
                        this.err = e;
                    }
                    throw e;
                }
            } else {
                numBytesWritten = copy(data, this.buf, this.usedBufferBytes);
                this.usedBufferBytes += numBytesWritten;
                this.flush();
            }
            totalBytesWritten += numBytesWritten;
            data = data.subarray(numBytesWritten);
        }
        numBytesWritten = copy(data, this.buf, this.usedBufferBytes);
        this.usedBufferBytes += numBytesWritten;
        totalBytesWritten += numBytesWritten;
        return totalBytesWritten;
    }
}
function createLPS(pat) {
    const lps = new Uint8Array(pat.length);
    lps[0] = 0;
    let prefixEnd = 0;
    let i = 1;
    while(i < lps.length){
        if (pat[i] == pat[prefixEnd]) {
            prefixEnd++;
            lps[i] = prefixEnd;
            i++;
        } else if (prefixEnd === 0) {
            lps[i] = 0;
            i++;
        } else {
            prefixEnd = lps[prefixEnd - 1];
        }
    }
    return lps;
}
async function* readDelim(reader, delim) {
    const delimLen = delim.length;
    const delimLPS = createLPS(delim);
    const chunks = new BytesList();
    const bufSize = Math.max(1024, delimLen + 1);
    let inspectIndex = 0;
    let matchIndex = 0;
    while(true){
        const inspectArr = new Uint8Array(bufSize);
        const result = await reader.read(inspectArr);
        if (result === null) {
            yield chunks.concat();
            return;
        } else if (result < 0) {
            return;
        }
        chunks.add(inspectArr, 0, result);
        let localIndex = 0;
        while(inspectIndex < chunks.size()){
            if (inspectArr[localIndex] === delim[matchIndex]) {
                inspectIndex++;
                localIndex++;
                matchIndex++;
                if (matchIndex === delimLen) {
                    const matchEnd = inspectIndex - delimLen;
                    const readyBytes = chunks.slice(0, matchEnd);
                    yield readyBytes;
                    chunks.shift(inspectIndex);
                    inspectIndex = 0;
                    matchIndex = 0;
                }
            } else {
                if (matchIndex === 0) {
                    inspectIndex++;
                    localIndex++;
                } else {
                    matchIndex = delimLPS[matchIndex - 1];
                }
            }
        }
    }
}
async function* readStringDelim(reader, delim, decoderOpts) {
    const encoder = new TextEncoder();
    const decoder = new TextDecoder(decoderOpts?.encoding, decoderOpts);
    for await (const chunk of readDelim(reader, encoder.encode(delim))){
        yield decoder.decode(chunk);
    }
}
async function* readLines(reader, decoderOpts) {
    const bufReader = new BufReader(reader);
    let chunks = [];
    const decoder = new TextDecoder(decoderOpts?.encoding, decoderOpts);
    while(true){
        const res = await bufReader.readLine();
        if (!res) {
            if (chunks.length > 0) {
                yield decoder.decode(concat(...chunks));
            }
            break;
        }
        chunks.push(res.line);
        if (!res.more) {
            yield decoder.decode(concat(...chunks));
            chunks = [];
        }
    }
}
class StringReader extends Buffer {
    constructor(s){
        super(new TextEncoder().encode(s).buffer);
    }
}
class MultiReader {
    #readers;
    #currentIndex = 0;
    constructor(readers){
        this.#readers = [
            ...readers
        ];
    }
    async read(p) {
        const r = this.#readers[this.#currentIndex];
        if (!r) return null;
        const result = await r.read(p);
        if (result === null) {
            this.#currentIndex++;
            return 0;
        }
        return result;
    }
}
class LimitedReader {
    reader;
    limit;
    constructor(reader, limit){
        this.reader = reader;
        this.limit = limit;
    }
    async read(p) {
        if (this.limit <= 0) {
            return null;
        }
        if (p.length > this.limit) {
            p = p.subarray(0, this.limit);
        }
        const n = await this.reader.read(p);
        if (n == null) {
            return null;
        }
        this.limit -= n;
        return n;
    }
}
const DEFAULT_BUFFER_SIZE = 32 * 1024;
async function copyN(r, dest, size) {
    let bytesRead = 0;
    let buf = new Uint8Array(DEFAULT_BUFFER_SIZE);
    while(bytesRead < size){
        if (size - bytesRead < DEFAULT_BUFFER_SIZE) {
            buf = new Uint8Array(size - bytesRead);
        }
        const result = await r.read(buf);
        const nread = result ?? 0;
        bytesRead += nread;
        if (nread > 0) {
            let n = 0;
            while(n < nread){
                n += await dest.write(buf.slice(n, nread));
            }
            assert(n === nread, "could not write");
        }
        if (result === null) {
            break;
        }
    }
    return bytesRead;
}
async function readShort(buf) {
    const high = await buf.readByte();
    if (high === null) return null;
    const low = await buf.readByte();
    if (low === null) throw new Deno.errors.UnexpectedEof();
    return high << 8 | low;
}
async function readInt(buf) {
    const high = await readShort(buf);
    if (high === null) return null;
    const low = await readShort(buf);
    if (low === null) throw new Deno.errors.UnexpectedEof();
    return high << 16 | low;
}
const MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
async function readLong(buf) {
    const high = await readInt(buf);
    if (high === null) return null;
    const low = await readInt(buf);
    if (low === null) throw new Deno.errors.UnexpectedEof();
    const big = BigInt(high) << 32n | BigInt(low);
    if (big > MAX_SAFE_INTEGER) {
        throw new RangeError("Long value too big to be represented as a JavaScript number.");
    }
    return Number(big);
}
function sliceLongToBytes(d, dest = Array.from({
    length: 8
})) {
    let big = BigInt(d);
    for(let i = 0; i < 8; i++){
        dest[7 - i] = Number(big & 0xffn);
        big >>= 8n;
    }
    return dest;
}
const decoder = new TextDecoder();
class StringWriter {
    base;
    #chunks;
    #byteLength;
    #cache;
    constructor(base = ""){
        this.base = base;
        this.#chunks = [];
        this.#byteLength = 0;
        const c = new TextEncoder().encode(base);
        this.#chunks.push(c);
        this.#byteLength += c.byteLength;
    }
    write(p) {
        return Promise.resolve(this.writeSync(p));
    }
    writeSync(p) {
        this.#chunks.push(new Uint8Array(p));
        this.#byteLength += p.byteLength;
        this.#cache = undefined;
        return p.byteLength;
    }
    toString() {
        if (this.#cache) {
            return this.#cache;
        }
        const buf = new Uint8Array(this.#byteLength);
        let offs = 0;
        for (const chunk of this.#chunks){
            buf.set(chunk, offs);
            offs += chunk.byteLength;
        }
        this.#cache = decoder.decode(buf);
        return this.#cache;
    }
}
const mod2 = {
    copyN: copyN,
    readInt: readInt,
    readLong: readLong,
    readShort: readShort,
    sliceLongToBytes: sliceLongToBytes,
    Buffer,
    BufferFullError,
    PartialReadError,
    BufReader,
    BufWriter,
    BufWriterSync,
    readDelim,
    readStringDelim,
    readLines,
    StringReader,
    MultiReader,
    LimitedReader,
    StringWriter
};
function bytesToUuid(bytes) {
    const bits = [
        ...bytes
    ].map((bit)=>{
        const s = bit.toString(16);
        return bit < 0x10 ? "0" + s : s;
    });
    return [
        ...bits.slice(0, 4),
        "-",
        ...bits.slice(4, 6),
        "-",
        ...bits.slice(6, 8),
        "-",
        ...bits.slice(8, 10),
        "-",
        ...bits.slice(10, 16)
    ].join("");
}
function uuidToBytes(uuid) {
    const bytes = [];
    uuid.replace(/[a-fA-F0-9]{2}/g, (hex)=>{
        bytes.push(parseInt(hex, 16));
        return "";
    });
    return bytes;
}
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-1[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
function validate(id) {
    return UUID_RE.test(id);
}
let _nodeId;
let _clockseq;
let _lastMSecs = 0;
let _lastNSecs = 0;
function generate(options, buf, offset) {
    let i = buf && offset || 0;
    const b = buf ?? [];
    options ??= {};
    let { node =_nodeId , clockseq =_clockseq  } = options;
    if (node === undefined || clockseq === undefined) {
        const seedBytes = options.random ?? options.rng ?? crypto.getRandomValues(new Uint8Array(16));
        if (node === undefined) {
            node = _nodeId = [
                seedBytes[0] | 0x01,
                seedBytes[1],
                seedBytes[2],
                seedBytes[3],
                seedBytes[4],
                seedBytes[5]
            ];
        }
        if (clockseq === undefined) {
            clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff;
        }
    }
    let { msecs =new Date().getTime() , nsecs =_lastNSecs + 1  } = options;
    const dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000;
    if (dt < 0 && options.clockseq === undefined) {
        clockseq = clockseq + 1 & 0x3fff;
    }
    if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {
        nsecs = 0;
    }
    if (nsecs > 10000) {
        throw new Error("Can't create more than 10M uuids/sec");
    }
    _lastMSecs = msecs;
    _lastNSecs = nsecs;
    _clockseq = clockseq;
    msecs += 12219292800000;
    const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
    b[i++] = tl >>> 24 & 0xff;
    b[i++] = tl >>> 16 & 0xff;
    b[i++] = tl >>> 8 & 0xff;
    b[i++] = tl & 0xff;
    const tmh = msecs / 0x100000000 * 10000 & 0xfffffff;
    b[i++] = tmh >>> 8 & 0xff;
    b[i++] = tmh & 0xff;
    b[i++] = tmh >>> 24 & 0xf | 0x10;
    b[i++] = tmh >>> 16 & 0xff;
    b[i++] = clockseq >>> 8 | 0x80;
    b[i++] = clockseq & 0xff;
    for(let n = 0; n < 6; ++n){
        b[i + n] = node[n];
    }
    return buf ?? bytesToUuid(b);
}
const mod3 = {
    validate: validate,
    generate: generate
};
const UUID_RE1 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
function validate1(id) {
    return UUID_RE1.test(id);
}
const mod4 = {
    validate: validate1
};
const UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
function validate2(id) {
    return UUID_RE2.test(id);
}
async function generate1(namespace, data) {
    const space = uuidToBytes(namespace);
    assert(space.length === 16, "namespace must be a valid UUID");
    const toHash = concat(new Uint8Array(space), data);
    const buffer = await crypto.subtle.digest("sha-1", toHash);
    const bytes = new Uint8Array(buffer);
    bytes[6] = bytes[6] & 0x0f | 0x50;
    bytes[8] = bytes[8] & 0x3f | 0x80;
    return bytesToUuid(bytes);
}
const mod5 = {
    validate: validate2,
    generate: generate1
};
const NIL_UUID = "00000000-0000-0000-0000-000000000000";
function isNil(id) {
    return id === NIL_UUID;
}
function validate3(uuid) {
    return /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i.test(uuid);
}
function version(uuid) {
    if (!validate3(uuid)) {
        throw new TypeError("Invalid UUID");
    }
    return parseInt(uuid[14], 16);
}
const mod6 = {
    v1: mod3,
    v4: mod4,
    v5: mod5,
    NIL_UUID: NIL_UUID,
    isNil: isNil,
    validate: validate3,
    version: version
};
function utf8Count(str) {
    const strLength = str.length;
    let byteLength = 0;
    let pos = 0;
    while(pos < strLength){
        let value = str.charCodeAt(pos++);
        if ((value & 0xffffff80) === 0) {
            byteLength++;
            continue;
        } else if ((value & 0xfffff800) === 0) {
            byteLength += 2;
        } else {
            if (value >= 0xd800 && value <= 0xdbff) {
                if (pos < strLength) {
                    const extra = str.charCodeAt(pos);
                    if ((extra & 0xfc00) === 0xdc00) {
                        ++pos;
                        value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000;
                    }
                }
            }
            if ((value & 0xffff0000) === 0) {
                byteLength += 3;
            } else {
                byteLength += 4;
            }
        }
    }
    return byteLength;
}
function utf8EncodeJs(str, output, outputOffset) {
    const strLength = str.length;
    let offset = outputOffset;
    let pos = 0;
    while(pos < strLength){
        let value = str.charCodeAt(pos++);
        if ((value & 0xffffff80) === 0) {
            output[offset++] = value;
            continue;
        } else if ((value & 0xfffff800) === 0) {
            output[offset++] = value >> 6 & 0x1f | 0xc0;
        } else {
            if (value >= 0xd800 && value <= 0xdbff) {
                if (pos < strLength) {
                    const extra = str.charCodeAt(pos);
                    if ((extra & 0xfc00) === 0xdc00) {
                        ++pos;
                        value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000;
                    }
                }
            }
            if ((value & 0xffff0000) === 0) {
                output[offset++] = value >> 12 & 0x0f | 0xe0;
                output[offset++] = value >> 6 & 0x3f | 0x80;
            } else {
                output[offset++] = value >> 18 & 0x07 | 0xf0;
                output[offset++] = value >> 12 & 0x3f | 0x80;
                output[offset++] = value >> 6 & 0x3f | 0x80;
            }
        }
        output[offset++] = value & 0x3f | 0x80;
    }
}
const sharedTextEncoder = new TextEncoder();
function utf8EncodeTEencodeInto(str, output, outputOffset) {
    sharedTextEncoder.encodeInto(str, output.subarray(outputOffset));
}
const utf8EncodeTE = utf8EncodeTEencodeInto;
function utf8DecodeJs(bytes, inputOffset, byteLength) {
    let offset = inputOffset;
    const end = offset + byteLength;
    const units = [];
    let result = "";
    while(offset < end){
        const byte1 = bytes[offset++];
        if ((byte1 & 0x80) === 0) {
            units.push(byte1);
        } else if ((byte1 & 0xe0) === 0xc0) {
            const byte2 = bytes[offset++] & 0x3f;
            units.push((byte1 & 0x1f) << 6 | byte2);
        } else if ((byte1 & 0xf0) === 0xe0) {
            const byte2 = bytes[offset++] & 0x3f;
            const byte3 = bytes[offset++] & 0x3f;
            units.push((byte1 & 0x1f) << 12 | byte2 << 6 | byte3);
        } else if ((byte1 & 0xf8) === 0xf0) {
            const byte2 = bytes[offset++] & 0x3f;
            const byte3 = bytes[offset++] & 0x3f;
            const byte4 = bytes[offset++] & 0x3f;
            let unit = (byte1 & 0x07) << 0x12 | byte2 << 0x0c | byte3 << 0x06 | byte4;
            if (unit > 0xffff) {
                unit -= 0x10000;
                units.push(unit >>> 10 & 0x3ff | 0xd800);
                unit = 0xdc00 | unit & 0x3ff;
            }
            units.push(unit);
        } else {
            units.push(byte1);
        }
        if (units.length >= 0x1_000) {
            result += String.fromCharCode(...units);
            units.length = 0;
        }
    }
    if (units.length > 0) {
        result += String.fromCharCode(...units);
    }
    return result;
}
const sharedTextDecoder = new TextDecoder();
function utf8DecodeTD(bytes, inputOffset, byteLength) {
    const stringBytes = bytes.subarray(inputOffset, inputOffset + byteLength);
    return sharedTextDecoder.decode(stringBytes);
}
class ExtData {
    type;
    data;
    constructor(type, data){
        this.type = type;
        this.data = data;
    }
}
function setUint64(view, offset, value) {
    const high = value / 0x1_0000_0000;
    const low = value;
    view.setUint32(offset, high);
    view.setUint32(offset + 4, low);
}
function setInt64(view, offset, value) {
    const high = Math.floor(value / 0x1_0000_0000);
    const low = value;
    view.setUint32(offset, high);
    view.setUint32(offset + 4, low);
}
function getInt64(view, offset) {
    const high = view.getInt32(offset);
    const low = view.getUint32(offset + 4);
    return high * 0x1_0000_0000 + low;
}
function getUint64(view, offset) {
    const high = view.getUint32(offset);
    const low = view.getUint32(offset + 4);
    return high * 0x1_0000_0000 + low;
}
const EXT_TIMESTAMP = -1;
const TIMESTAMP32_MAX_SEC = 0x100000000 - 1;
const TIMESTAMP64_MAX_SEC = 0x400000000 - 1;
function encodeTimeSpecToTimestamp({ sec , nsec  }) {
    if (sec >= 0 && nsec >= 0 && sec <= TIMESTAMP64_MAX_SEC) {
        if (nsec === 0 && sec <= TIMESTAMP32_MAX_SEC) {
            const rv = new Uint8Array(4);
            const view = new DataView(rv.buffer);
            view.setUint32(0, sec);
            return rv;
        } else {
            const secHigh = sec / 0x100000000;
            const secLow = sec & 0xffffffff;
            const rv = new Uint8Array(8);
            const view = new DataView(rv.buffer);
            view.setUint32(0, nsec << 2 | secHigh & 0x3);
            view.setUint32(4, secLow);
            return rv;
        }
    } else {
        const rv = new Uint8Array(12);
        const view = new DataView(rv.buffer);
        view.setUint32(0, nsec);
        setInt64(view, 4, sec);
        return rv;
    }
}
function encodeDateToTimeSpec(date) {
    const msec = date.getTime();
    const sec = Math.floor(msec / 1e3);
    const nsec = (msec - sec * 1e3) * 1e6;
    const nsecInSec = Math.floor(nsec / 1e9);
    return {
        sec: sec + nsecInSec,
        nsec: nsec - nsecInSec * 1e9
    };
}
function encodeTimestampExtension(object) {
    if (object instanceof Date) {
        const timeSpec = encodeDateToTimeSpec(object);
        return encodeTimeSpecToTimestamp(timeSpec);
    } else {
        return null;
    }
}
function decodeTimestampToTimeSpec(data) {
    const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
    switch(data.byteLength){
        case 4:
            {
                const sec = view.getUint32(0);
                return {
                    sec,
                    nsec: 0
                };
            }
        case 8:
            {
                const nsec30AndSecHigh2 = view.getUint32(0);
                const secLow32 = view.getUint32(4);
                const sec = (nsec30AndSecHigh2 & 0x3) * 0x100000000 + secLow32;
                const nsec = nsec30AndSecHigh2 >>> 2;
                return {
                    sec,
                    nsec
                };
            }
        case 12:
            {
                const sec = getInt64(view, 4);
                const nsec = view.getUint32(0);
                return {
                    sec,
                    nsec
                };
            }
        default:
            throw new Error(`Unrecognized data size for timestamp: ${data.length}`);
    }
}
function decodeTimestampExtension(data) {
    const timeSpec = decodeTimestampToTimeSpec(data);
    return new Date(timeSpec.sec * 1e3 + timeSpec.nsec / 1e6);
}
const timestampExtension = {
    type: EXT_TIMESTAMP,
    encode: encodeTimestampExtension,
    decode: decodeTimestampExtension
};
class ExtensionCodec {
    static defaultCodec = new ExtensionCodec();
    __brand;
    builtInEncoders = [];
    builtInDecoders = [];
    encoders = [];
    decoders = [];
    constructor(){
        this.register(timestampExtension);
    }
    register({ type , encode , decode  }) {
        if (type >= 0) {
            this.encoders[type] = encode;
            this.decoders[type] = decode;
        } else {
            const index = 1 + type;
            this.builtInEncoders[index] = encode;
            this.builtInDecoders[index] = decode;
        }
    }
    tryToEncode(object, context) {
        for(let i = 0; i < this.builtInEncoders.length; i++){
            const encoder = this.builtInEncoders[i];
            if (encoder != null) {
                const data = encoder(object, context);
                if (data != null) {
                    const type = -1 - i;
                    return new ExtData(type, data);
                }
            }
        }
        for(let i = 0; i < this.encoders.length; i++){
            const encoder = this.encoders[i];
            if (encoder != null) {
                const data = encoder(object, context);
                if (data != null) {
                    const type = i;
                    return new ExtData(type, data);
                }
            }
        }
        if (object instanceof ExtData) {
            return object;
        }
        return null;
    }
    decode(data, type, context) {
        const decoder = type < 0 ? this.builtInDecoders[-1 - type] : this.decoders[type];
        if (decoder) {
            return decoder(data, type, context);
        } else {
            return new ExtData(type, data);
        }
    }
}
function ensureUint8Array(buffer) {
    if (buffer instanceof Uint8Array) {
        return buffer;
    } else if (ArrayBuffer.isView(buffer)) {
        return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
    } else if (buffer instanceof ArrayBuffer) {
        return new Uint8Array(buffer);
    } else {
        return Uint8Array.from(buffer);
    }
}
function createDataView(buffer) {
    if (buffer instanceof ArrayBuffer) {
        return new DataView(buffer);
    }
    const bufferView = ensureUint8Array(buffer);
    return new DataView(bufferView.buffer, bufferView.byteOffset, bufferView.byteLength);
}
class Encoder {
    extensionCodec;
    context;
    maxDepth;
    initialBufferSize;
    sortKeys;
    forceFloat32;
    ignoreUndefined;
    pos;
    view;
    bytes;
    constructor(extensionCodec = ExtensionCodec.defaultCodec, context = undefined, maxDepth = 100, initialBufferSize = 2048, sortKeys = false, forceFloat32 = false, ignoreUndefined = false){
        this.extensionCodec = extensionCodec;
        this.context = context;
        this.maxDepth = maxDepth;
        this.initialBufferSize = initialBufferSize;
        this.sortKeys = sortKeys;
        this.forceFloat32 = forceFloat32;
        this.ignoreUndefined = ignoreUndefined;
        this.pos = 0;
        this.view = new DataView(new ArrayBuffer(this.initialBufferSize));
        this.bytes = new Uint8Array(this.view.buffer);
    }
    getUint8Array() {
        return this.bytes.subarray(0, this.pos);
    }
    reinitializeState() {
        this.pos = 0;
    }
    encode(object) {
        this.reinitializeState();
        this.doEncode(object, 1);
        return this.getUint8Array();
    }
    doEncode(object, depth) {
        if (depth > this.maxDepth) {
            throw new Error(`Too deep objects in depth ${depth}`);
        }
        if (object == null) {
            this.encodeNil();
        } else if (typeof object === "boolean") {
            this.encodeBoolean(object);
        } else if (typeof object === "number") {
            this.encodeNumber(object);
        } else if (typeof object === "string") {
            this.encodeString(object);
        } else {
            this.encodeObject(object, depth);
        }
    }
    ensureBufferSizeToWrite(sizeToWrite) {
        const requiredSize = this.pos + sizeToWrite;
        if (this.view.byteLength < requiredSize) {
            this.resizeBuffer(requiredSize * 2);
        }
    }
    resizeBuffer(newSize) {
        const newBuffer = new ArrayBuffer(newSize);
        const newBytes = new Uint8Array(newBuffer);
        const newView = new DataView(newBuffer);
        newBytes.set(this.bytes);
        this.view = newView;
        this.bytes = newBytes;
    }
    encodeNil() {
        this.writeU8(0xc0);
    }
    encodeBoolean(object) {
        if (object === false) {
            this.writeU8(0xc2);
        } else {
            this.writeU8(0xc3);
        }
    }
    encodeNumber(object) {
        if (Number.isSafeInteger(object)) {
            if (object >= 0) {
                if (object < 0x80) {
                    this.writeU8(object);
                } else if (object < 0x100) {
                    this.writeU8(0xcc);
                    this.writeU8(object);
                } else if (object < 0x10000) {
                    this.writeU8(0xcd);
                    this.writeU16(object);
                } else if (object < 0x100000000) {
                    this.writeU8(0xce);
                    this.writeU32(object);
                } else {
                    this.writeU8(0xcf);
                    this.writeU64(object);
                }
            } else {
                if (object >= -0x20) {
                    this.writeU8(0xe0 | object + 0x20);
                } else if (object >= -0x80) {
                    this.writeU8(0xd0);
                    this.writeI8(object);
                } else if (object >= -0x8000) {
                    this.writeU8(0xd1);
                    this.writeI16(object);
                } else if (object >= -0x80000000) {
                    this.writeU8(0xd2);
                    this.writeI32(object);
                } else {
                    this.writeU8(0xd3);
                    this.writeI64(object);
                }
            }
        } else {
            if (this.forceFloat32) {
                this.writeU8(0xca);
                this.writeF32(object);
            } else {
                this.writeU8(0xcb);
                this.writeF64(object);
            }
        }
    }
    writeStringHeader(byteLength) {
        if (byteLength < 32) {
            this.writeU8(0xa0 + byteLength);
        } else if (byteLength < 0x100) {
            this.writeU8(0xd9);
            this.writeU8(byteLength);
        } else if (byteLength < 0x10000) {
            this.writeU8(0xda);
            this.writeU16(byteLength);
        } else if (byteLength < 0x100000000) {
            this.writeU8(0xdb);
            this.writeU32(byteLength);
        } else {
            throw new Error(`Too long string: ${byteLength} bytes in UTF-8`);
        }
    }
    encodeString(object) {
        const maxHeaderSize = 1 + 4;
        const strLength = object.length;
        if (strLength > 200) {
            const byteLength = utf8Count(object);
            this.ensureBufferSizeToWrite(maxHeaderSize + byteLength);
            this.writeStringHeader(byteLength);
            utf8EncodeTE(object, this.bytes, this.pos);
            this.pos += byteLength;
        } else {
            const byteLength = utf8Count(object);
            this.ensureBufferSizeToWrite(maxHeaderSize + byteLength);
            this.writeStringHeader(byteLength);
            utf8EncodeJs(object, this.bytes, this.pos);
            this.pos += byteLength;
        }
    }
    encodeObject(object, depth) {
        const ext = this.extensionCodec.tryToEncode(object, this.context);
        if (ext != null) {
            this.encodeExtension(ext);
        } else if (Array.isArray(object)) {
            this.encodeArray(object, depth);
        } else if (ArrayBuffer.isView(object)) {
            this.encodeBinary(object);
        } else if (typeof object === "object") {
            this.encodeMap(object, depth);
        } else {
            throw new Error(`Unrecognized object: ${Object.prototype.toString.apply(object)}`);
        }
    }
    encodeBinary(object) {
        const size = object.byteLength;
        if (size < 0x100) {
            this.writeU8(0xc4);
            this.writeU8(size);
        } else if (size < 0x10000) {
            this.writeU8(0xc5);
            this.writeU16(size);
        } else if (size < 0x100000000) {
            this.writeU8(0xc6);
            this.writeU32(size);
        } else {
            throw new Error(`Too large binary: ${size}`);
        }
        const bytes = ensureUint8Array(object);
        this.writeU8a(bytes);
    }
    encodeArray(object, depth) {
        const size = object.length;
        if (size < 16) {
            this.writeU8(0x90 + size);
        } else if (size < 0x10000) {
            this.writeU8(0xdc);
            this.writeU16(size);
        } else if (size < 0x100000000) {
            this.writeU8(0xdd);
            this.writeU32(size);
        } else {
            throw new Error(`Too large array: ${size}`);
        }
        for (const item of object){
            this.doEncode(item, depth + 1);
        }
    }
    countWithoutUndefined(object, keys) {
        let count = 0;
        for (const key of keys){
            if (object[key] !== undefined) {
                count++;
            }
        }
        return count;
    }
    encodeMap(object, depth) {
        const keys = Object.keys(object);
        if (this.sortKeys) {
            keys.sort();
        }
        const size = this.ignoreUndefined ? this.countWithoutUndefined(object, keys) : keys.length;
        if (size < 16) {
            this.writeU8(0x80 + size);
        } else if (size < 0x10000) {
            this.writeU8(0xde);
            this.writeU16(size);
        } else if (size < 0x100000000) {
            this.writeU8(0xdf);
            this.writeU32(size);
        } else {
            throw new Error(`Too large map object: ${size}`);
        }
        for (const key of keys){
            const value = object[key];
            if (!(this.ignoreUndefined && value === undefined)) {
                this.encodeString(key);
                this.doEncode(value, depth + 1);
            }
        }
    }
    encodeExtension(ext) {
        const size = ext.data.length;
        if (size === 1) {
            this.writeU8(0xd4);
        } else if (size === 2) {
            this.writeU8(0xd5);
        } else if (size === 4) {
            this.writeU8(0xd6);
        } else if (size === 8) {
            this.writeU8(0xd7);
        } else if (size === 16) {
            this.writeU8(0xd8);
        } else if (size < 0x100) {
            this.writeU8(0xc7);
            this.writeU8(size);
        } else if (size < 0x10000) {
            this.writeU8(0xc8);
            this.writeU16(size);
        } else if (size < 0x100000000) {
            this.writeU8(0xc9);
            this.writeU32(size);
        } else {
            throw new Error(`Too large extension object: ${size}`);
        }
        this.writeI8(ext.type);
        this.writeU8a(ext.data);
    }
    writeU8(value) {
        this.ensureBufferSizeToWrite(1);
        this.view.setUint8(this.pos, value);
        this.pos++;
    }
    writeU8a(values) {
        const size = values.length;
        this.ensureBufferSizeToWrite(size);
        this.bytes.set(values, this.pos);
        this.pos += size;
    }
    writeI8(value) {
        this.ensureBufferSizeToWrite(1);
        this.view.setInt8(this.pos, value);
        this.pos++;
    }
    writeU16(value) {
        this.ensureBufferSizeToWrite(2);
        this.view.setUint16(this.pos, value);
        this.pos += 2;
    }
    writeI16(value) {
        this.ensureBufferSizeToWrite(2);
        this.view.setInt16(this.pos, value);
        this.pos += 2;
    }
    writeU32(value) {
        this.ensureBufferSizeToWrite(4);
        this.view.setUint32(this.pos, value);
        this.pos += 4;
    }
    writeI32(value) {
        this.ensureBufferSizeToWrite(4);
        this.view.setInt32(this.pos, value);
        this.pos += 4;
    }
    writeF32(value) {
        this.ensureBufferSizeToWrite(4);
        this.view.setFloat32(this.pos, value);
        this.pos += 4;
    }
    writeF64(value) {
        this.ensureBufferSizeToWrite(8);
        this.view.setFloat64(this.pos, value);
        this.pos += 8;
    }
    writeU64(value) {
        this.ensureBufferSizeToWrite(8);
        setUint64(this.view, this.pos, value);
        this.pos += 8;
    }
    writeI64(value) {
        this.ensureBufferSizeToWrite(8);
        setInt64(this.view, this.pos, value);
        this.pos += 8;
    }
}
const defaultEncodeOptions = {};
function encode1(value, options = defaultEncodeOptions) {
    const encoder = new Encoder(options.extensionCodec, options.context, options.maxDepth, options.initialBufferSize, options.sortKeys, options.forceFloat32, options.ignoreUndefined);
    return encoder.encode(value);
}
function prettyByte(__byte) {
    return `${__byte < 0 ? "-" : ""}0x${Math.abs(__byte).toString(16).padStart(2, "0")}`;
}
class CachedKeyDecoder {
    maxKeyLength;
    maxLengthPerKey;
    hit;
    miss;
    caches;
    constructor(maxKeyLength = 16, maxLengthPerKey = 16){
        this.maxKeyLength = maxKeyLength;
        this.maxLengthPerKey = maxLengthPerKey;
        this.hit = 0;
        this.miss = 0;
        this.caches = [];
        for(let i = 0; i < this.maxKeyLength; i++){
            this.caches.push([]);
        }
    }
    canBeCached(byteLength) {
        return byteLength > 0 && byteLength <= this.maxKeyLength;
    }
    get(bytes, inputOffset, byteLength) {
        const records = this.caches[byteLength - 1];
        const recordsLength = records.length;
        FIND_CHUNK: for(let i = 0; i < recordsLength; i++){
            const record = records[i];
            const recordBytes = record.bytes;
            for(let j = 0; j < byteLength; j++){
                if (recordBytes[j] !== bytes[inputOffset + j]) {
                    continue FIND_CHUNK;
                }
            }
            return record.value;
        }
        return null;
    }
    store(bytes, value) {
        const records = this.caches[bytes.length - 1];
        const record = {
            bytes,
            value
        };
        if (records.length >= this.maxLengthPerKey) {
            records[Math.random() * records.length | 0] = record;
        } else {
            records.push(record);
        }
    }
    decode(bytes, inputOffset, byteLength) {
        const cachedValue = this.get(bytes, inputOffset, byteLength);
        if (cachedValue != null) {
            this.hit++;
            return cachedValue;
        }
        this.miss++;
        const value = utf8DecodeJs(bytes, inputOffset, byteLength);
        const slicedCopyOfBytes = Uint8Array.prototype.slice.call(bytes, inputOffset, inputOffset + byteLength);
        this.store(slicedCopyOfBytes, value);
        return value;
    }
}
var State;
(function(State) {
    State[State["ARRAY"] = 0] = "ARRAY";
    State[State["MAP_KEY"] = 1] = "MAP_KEY";
    State[State["MAP_VALUE"] = 2] = "MAP_VALUE";
})(State || (State = {}));
const isValidMapKeyType = (key)=>{
    const keyType = typeof key;
    return keyType === "string" || keyType === "number";
};
const HEAD_BYTE_REQUIRED = -1;
const EMPTY_VIEW = new DataView(new ArrayBuffer(0));
const EMPTY_BYTES = new Uint8Array(EMPTY_VIEW.buffer);
const DataViewIndexOutOfBoundsError = (()=>{
    try {
        EMPTY_VIEW.getInt8(0);
    } catch (e) {
        return e.constructor;
    }
    throw new Error("never reached");
})();
const MORE_DATA = new DataViewIndexOutOfBoundsError("Insufficient data");
const sharedCachedKeyDecoder = new CachedKeyDecoder();
class Decoder {
    extensionCodec;
    context;
    maxStrLength;
    maxBinLength;
    maxArrayLength;
    maxMapLength;
    maxExtLength;
    keyDecoder;
    totalPos;
    pos;
    view;
    bytes;
    headByte;
    stack;
    constructor(extensionCodec = ExtensionCodec.defaultCodec, context = undefined, maxStrLength = 0xffff_ffff, maxBinLength = 0xffff_ffff, maxArrayLength = 0xffff_ffff, maxMapLength = 0xffff_ffff, maxExtLength = 0xffff_ffff, keyDecoder = sharedCachedKeyDecoder){
        this.extensionCodec = extensionCodec;
        this.context = context;
        this.maxStrLength = maxStrLength;
        this.maxBinLength = maxBinLength;
        this.maxArrayLength = maxArrayLength;
        this.maxMapLength = maxMapLength;
        this.maxExtLength = maxExtLength;
        this.keyDecoder = keyDecoder;
        this.totalPos = 0;
        this.pos = 0;
        this.view = EMPTY_VIEW;
        this.bytes = EMPTY_BYTES;
        this.headByte = HEAD_BYTE_REQUIRED;
        this.stack = [];
    }
    reinitializeState() {
        this.totalPos = 0;
        this.headByte = HEAD_BYTE_REQUIRED;
    }
    setBuffer(buffer) {
        this.bytes = ensureUint8Array(buffer);
        this.view = createDataView(this.bytes);
        this.pos = 0;
    }
    appendBuffer(buffer) {
        buffer = ensureUint8Array(buffer).slice();
        if (this.headByte === HEAD_BYTE_REQUIRED && !this.hasRemaining()) {
            this.setBuffer(buffer);
        } else {
            const remainingData = this.bytes.subarray(this.pos);
            const newData = ensureUint8Array(buffer);
            const concated = new Uint8Array(remainingData.length + newData.length);
            concated.set(remainingData);
            concated.set(newData, remainingData.length);
            this.setBuffer(concated);
        }
    }
    hasRemaining(size = 1) {
        return this.view.byteLength - this.pos >= size;
    }
    createNoExtraBytesError(posToShow) {
        const { view , pos  } = this;
        return new RangeError(`Extra ${view.byteLength - pos} of ${view.byteLength} byte(s) found at buffer[${posToShow}]`);
    }
    decode(buffer) {
        this.reinitializeState();
        this.setBuffer(buffer);
        return this.doDecodeSingleSync();
    }
    doDecodeSingleSync() {
        const object = this.doDecodeSync();
        if (this.hasRemaining()) {
            throw this.createNoExtraBytesError(this.pos);
        }
        return object;
    }
    async decodeAsync(stream) {
        let decoded = false;
        let object;
        for await (const buffer of stream){
            if (decoded) {
                throw this.createNoExtraBytesError(this.totalPos);
            }
            this.appendBuffer(buffer);
            try {
                object = this.doDecodeSync();
                decoded = true;
            } catch (e) {
                if (!(e instanceof DataViewIndexOutOfBoundsError)) {
                    throw e;
                }
            }
            this.totalPos += this.pos;
        }
        if (decoded) {
            if (this.hasRemaining()) {
                throw this.createNoExtraBytesError(this.totalPos);
            }
            return object;
        }
        const { headByte , pos , totalPos  } = this;
        throw new RangeError(`Insufficient data in parcing ${prettyByte(headByte)} at ${totalPos} (${pos} in the current buffer)`);
    }
    decodeArrayStream(stream) {
        return this.decodeMultiAsync(stream, true);
    }
    decodeStream(stream) {
        return this.decodeMultiAsync(stream, false);
    }
    async *decodeMultiAsync(stream, isArray) {
        let isArrayHeaderRequired = isArray;
        let arrayItemsLeft = -1;
        for await (const buffer of stream){
            if (isArray && arrayItemsLeft === 0) {
                throw this.createNoExtraBytesError(this.totalPos);
            }
            this.appendBuffer(buffer);
            if (isArrayHeaderRequired) {
                arrayItemsLeft = this.readArraySize();
                isArrayHeaderRequired = false;
                this.complete();
            }
            try {
                while(true){
                    yield this.doDecodeSync();
                    if (--arrayItemsLeft === 0) {
                        break;
                    }
                }
            } catch (e) {
                if (!(e instanceof DataViewIndexOutOfBoundsError)) {
                    throw e;
                }
            }
            this.totalPos += this.pos;
        }
    }
    doDecodeSync() {
        DECODE: while(true){
            const headByte = this.readHeadByte();
            let object;
            if (headByte >= 0xe0) {
                object = headByte - 0x100;
            } else if (headByte < 0xc0) {
                if (headByte < 0x80) {
                    object = headByte;
                } else if (headByte < 0x90) {
                    const size = headByte - 0x80;
                    if (size !== 0) {
                        this.pushMapState(size);
                        this.complete();
                        continue DECODE;
                    } else {
                        object = {};
                    }
                } else if (headByte < 0xa0) {
                    const size = headByte - 0x90;
                    if (size !== 0) {
                        this.pushArrayState(size);
                        this.complete();
                        continue DECODE;
                    } else {
                        object = [];
                    }
                } else {
                    const byteLength = headByte - 0xa0;
                    object = this.decodeUtf8String(byteLength, 0);
                }
            } else if (headByte === 0xc0) {
                object = null;
            } else if (headByte === 0xc2) {
                object = false;
            } else if (headByte === 0xc3) {
                object = true;
            } else if (headByte === 0xca) {
                object = this.readF32();
            } else if (headByte === 0xcb) {
                object = this.readF64();
            } else if (headByte === 0xcc) {
                object = this.readU8();
            } else if (headByte === 0xcd) {
                object = this.readU16();
            } else if (headByte === 0xce) {
                object = this.readU32();
            } else if (headByte === 0xcf) {
                object = this.readU64();
            } else if (headByte === 0xd0) {
                object = this.readI8();
            } else if (headByte === 0xd1) {
                object = this.readI16();
            } else if (headByte === 0xd2) {
                object = this.readI32();
            } else if (headByte === 0xd3) {
                object = this.readI64();
            } else if (headByte === 0xd9) {
                const byteLength = this.lookU8();
                object = this.decodeUtf8String(byteLength, 1);
            } else if (headByte === 0xda) {
                const byteLength = this.lookU16();
                object = this.decodeUtf8String(byteLength, 2);
            } else if (headByte === 0xdb) {
                const byteLength = this.lookU32();
                object = this.decodeUtf8String(byteLength, 4);
            } else if (headByte === 0xdc) {
                const size = this.readU16();
                if (size !== 0) {
                    this.pushArrayState(size);
                    this.complete();
                    continue DECODE;
                } else {
                    object = [];
                }
            } else if (headByte === 0xdd) {
                const size = this.readU32();
                if (size !== 0) {
                    this.pushArrayState(size);
                    this.complete();
                    continue DECODE;
                } else {
                    object = [];
                }
            } else if (headByte === 0xde) {
                const size = this.readU16();
                if (size !== 0) {
                    this.pushMapState(size);
                    this.complete();
                    continue DECODE;
                } else {
                    object = {};
                }
            } else if (headByte === 0xdf) {
                const size = this.readU32();
                if (size !== 0) {
                    this.pushMapState(size);
                    this.complete();
                    continue DECODE;
                } else {
                    object = {};
                }
            } else if (headByte === 0xc4) {
                const size = this.lookU8();
                object = this.decodeBinary(size, 1);
            } else if (headByte === 0xc5) {
                const size = this.lookU16();
                object = this.decodeBinary(size, 2);
            } else if (headByte === 0xc6) {
                const size = this.lookU32();
                object = this.decodeBinary(size, 4);
            } else if (headByte === 0xd4) {
                object = this.decodeExtension(1, 0);
            } else if (headByte === 0xd5) {
                object = this.decodeExtension(2, 0);
            } else if (headByte === 0xd6) {
                object = this.decodeExtension(4, 0);
            } else if (headByte === 0xd7) {
                object = this.decodeExtension(8, 0);
            } else if (headByte === 0xd8) {
                object = this.decodeExtension(16, 0);
            } else if (headByte === 0xc7) {
                const size = this.lookU8();
                object = this.decodeExtension(size, 1);
            } else if (headByte === 0xc8) {
                const size = this.lookU16();
                object = this.decodeExtension(size, 2);
            } else if (headByte === 0xc9) {
                const size = this.lookU32();
                object = this.decodeExtension(size, 4);
            } else {
                throw new Error(`Unrecognized type byte: ${prettyByte(headByte)}`);
            }
            this.complete();
            const stack = this.stack;
            while(stack.length > 0){
                const state = stack[stack.length - 1];
                if (state.type === 0) {
                    state.array[state.position] = object;
                    state.position++;
                    if (state.position === state.size) {
                        stack.pop();
                        object = state.array;
                    } else {
                        continue DECODE;
                    }
                } else if (state.type === 1) {
                    if (!isValidMapKeyType(object)) {
                        throw new Error("The type of key must be string or number but " + typeof object);
                    }
                    state.key = object;
                    state.type = 2;
                    continue DECODE;
                } else {
                    state.map[state.key] = object;
                    state.readCount++;
                    if (state.readCount === state.size) {
                        stack.pop();
                        object = state.map;
                    } else {
                        state.key = null;
                        state.type = 1;
                        continue DECODE;
                    }
                }
            }
            return object;
        }
    }
    readHeadByte() {
        if (this.headByte === HEAD_BYTE_REQUIRED) {
            this.headByte = this.readU8();
        }
        return this.headByte;
    }
    complete() {
        this.headByte = HEAD_BYTE_REQUIRED;
    }
    readArraySize() {
        const headByte = this.readHeadByte();
        switch(headByte){
            case 0xdc:
                return this.readU16();
            case 0xdd:
                return this.readU32();
            default:
                {
                    if (headByte < 0xa0) {
                        return headByte - 0x90;
                    } else {
                        throw new Error(`Unrecognized array type byte: ${prettyByte(headByte)}`);
                    }
                }
        }
    }
    pushMapState(size) {
        if (size > this.maxMapLength) {
            throw new Error(`Max length exceeded: map length (${size}) > maxMapLengthLength (${this.maxMapLength})`);
        }
        this.stack.push({
            type: 1,
            size,
            key: null,
            readCount: 0,
            map: {}
        });
    }
    pushArrayState(size) {
        if (size > this.maxArrayLength) {
            throw new Error(`Max length exceeded: array length (${size}) > maxArrayLength (${this.maxArrayLength})`);
        }
        this.stack.push({
            type: 0,
            size,
            array: new Array(size),
            position: 0
        });
    }
    decodeUtf8String(byteLength, headerOffset) {
        if (byteLength > this.maxStrLength) {
            throw new Error(`Max length exceeded: UTF-8 byte length (${byteLength}) > maxStrLength (${this.maxStrLength})`);
        }
        if (this.bytes.byteLength < this.pos + headerOffset + byteLength) {
            throw MORE_DATA;
        }
        const offset = this.pos + headerOffset;
        let object;
        if (this.stateIsMapKey() && this.keyDecoder?.canBeCached(byteLength)) {
            object = this.keyDecoder.decode(this.bytes, offset, byteLength);
        } else if (byteLength > 200) {
            object = utf8DecodeTD(this.bytes, offset, byteLength);
        } else {
            object = utf8DecodeJs(this.bytes, offset, byteLength);
        }
        this.pos += headerOffset + byteLength;
        return object;
    }
    stateIsMapKey() {
        if (this.stack.length > 0) {
            const state = this.stack[this.stack.length - 1];
            return state.type === 1;
        }
        return false;
    }
    decodeBinary(byteLength, headOffset) {
        if (byteLength > this.maxBinLength) {
            throw new Error(`Max length exceeded: bin length (${byteLength}) > maxBinLength (${this.maxBinLength})`);
        }
        if (!this.hasRemaining(byteLength + headOffset)) {
            throw MORE_DATA;
        }
        const offset = this.pos + headOffset;
        const object = this.bytes.subarray(offset, offset + byteLength);
        this.pos += headOffset + byteLength;
        return object;
    }
    decodeExtension(size, headOffset) {
        if (size > this.maxExtLength) {
            throw new Error(`Max length exceeded: ext length (${size}) > maxExtLength (${this.maxExtLength})`);
        }
        const extType = this.view.getInt8(this.pos + headOffset);
        const data = this.decodeBinary(size, headOffset + 1);
        return this.extensionCodec.decode(data, extType, this.context);
    }
    lookU8() {
        return this.view.getUint8(this.pos);
    }
    lookU16() {
        return this.view.getUint16(this.pos);
    }
    lookU32() {
        return this.view.getUint32(this.pos);
    }
    readU8() {
        const value = this.view.getUint8(this.pos);
        this.pos++;
        return value;
    }
    readI8() {
        const value = this.view.getInt8(this.pos);
        this.pos++;
        return value;
    }
    readU16() {
        const value = this.view.getUint16(this.pos);
        this.pos += 2;
        return value;
    }
    readI16() {
        const value = this.view.getInt16(this.pos);
        this.pos += 2;
        return value;
    }
    readU32() {
        const value = this.view.getUint32(this.pos);
        this.pos += 4;
        return value;
    }
    readI32() {
        const value = this.view.getInt32(this.pos);
        this.pos += 4;
        return value;
    }
    readU64() {
        const value = getUint64(this.view, this.pos);
        this.pos += 8;
        return value;
    }
    readI64() {
        const value = getInt64(this.view, this.pos);
        this.pos += 8;
        return value;
    }
    readF32() {
        const value = this.view.getFloat32(this.pos);
        this.pos += 4;
        return value;
    }
    readF64() {
        const value = this.view.getFloat64(this.pos);
        this.pos += 8;
        return value;
    }
}
const defaultDecodeOptions = {};
function decode1(buffer, options = defaultDecodeOptions) {
    const decoder = new Decoder(options.extensionCodec, options.context, options.maxStrLength, options.maxBinLength, options.maxArrayLength, options.maxMapLength, options.maxExtLength);
    return decoder.decode(buffer);
}
function isAsyncIterable(object) {
    return object[Symbol.asyncIterator] != null;
}
function assertNonNull(value) {
    if (value == null) {
        throw new Error("Assertion Failure: value must not be null nor undefined");
    }
}
async function* asyncIterableFromStream(stream) {
    const reader = stream.getReader();
    try {
        while(true){
            const { done , value  } = await reader.read();
            if (done) {
                return;
            }
            assertNonNull(value);
            yield value;
        }
    } finally{
        reader.releaseLock();
    }
}
function ensureAsyncIterabe(streamLike) {
    if (isAsyncIterable(streamLike)) {
        return streamLike;
    } else {
        return asyncIterableFromStream(streamLike);
    }
}
function decodeAsync(streamLike, options = defaultDecodeOptions) {
    const stream = ensureAsyncIterabe(streamLike);
    const decoder = new Decoder(options.extensionCodec, options.context, options.maxStrLength, options.maxBinLength, options.maxArrayLength, options.maxMapLength, options.maxExtLength);
    return decoder.decodeAsync(stream);
}
function decodeArrayStream(streamLike, options = defaultDecodeOptions) {
    const stream = ensureAsyncIterabe(streamLike);
    const decoder = new Decoder(options.extensionCodec, options.context, options.maxStrLength, options.maxBinLength, options.maxArrayLength, options.maxMapLength, options.maxExtLength);
    return decoder.decodeArrayStream(stream);
}
function decodeStream(streamLike, options = defaultDecodeOptions) {
    const stream = ensureAsyncIterabe(streamLike);
    const decoder = new Decoder(options.extensionCodec, options.context, options.maxStrLength, options.maxBinLength, options.maxArrayLength, options.maxMapLength, options.maxExtLength);
    return decoder.decodeStream(stream);
}
const mod7 = {
    encode: encode1,
    decode: decode1,
    decodeArrayStream: decodeArrayStream,
    decodeAsync: decodeAsync,
    decodeStream: decodeStream,
    Decoder: Decoder,
    Encoder: Encoder,
    ExtensionCodec: ExtensionCodec,
    ExtData: ExtData,
    decodeTimestampExtension: decodeTimestampExtension,
    decodeTimestampToTimeSpec: decodeTimestampToTimeSpec,
    encodeDateToTimeSpec: encodeDateToTimeSpec,
    encodeTimeSpecToTimestamp: encodeTimeSpecToTimestamp,
    encodeTimestampExtension: encodeTimestampExtension,
    EXT_TIMESTAMP: EXT_TIMESTAMP
};
const osType = (()=>{
    const { Deno: Deno1  } = globalThis;
    if (typeof Deno1?.build?.os === "string") {
        return Deno1.build.os;
    }
    const { navigator  } = globalThis;
    if (navigator?.appVersion?.includes?.("Win")) {
        return "windows";
    }
    return "linux";
})();
const isWindows = osType === "windows";
const CHAR_FORWARD_SLASH = 47;
function assertPath(path) {
    if (typeof path !== "string") {
        throw new TypeError(`Path must be a string. Received ${JSON.stringify(path)}`);
    }
}
function isPosixPathSeparator(code) {
    return code === 47;
}
function isPathSeparator(code) {
    return isPosixPathSeparator(code) || code === 92;
}
function isWindowsDeviceRoot(code) {
    return code >= 97 && code <= 122 || code >= 65 && code <= 90;
}
function normalizeString(path, allowAboveRoot, separator, isPathSeparator) {
    let res = "";
    let lastSegmentLength = 0;
    let lastSlash = -1;
    let dots = 0;
    let code;
    for(let i = 0, len = path.length; i <= len; ++i){
        if (i < len) code = path.charCodeAt(i);
        else if (isPathSeparator(code)) break;
        else code = CHAR_FORWARD_SLASH;
        if (isPathSeparator(code)) {
            if (lastSlash === i - 1 || dots === 1) {} else if (lastSlash !== i - 1 && dots === 2) {
                if (res.length < 2 || lastSegmentLength !== 2 || res.charCodeAt(res.length - 1) !== 46 || res.charCodeAt(res.length - 2) !== 46) {
                    if (res.length > 2) {
                        const lastSlashIndex = res.lastIndexOf(separator);
                        if (lastSlashIndex === -1) {
                            res = "";
                            lastSegmentLength = 0;
                        } else {
                            res = res.slice(0, lastSlashIndex);
                            lastSegmentLength = res.length - 1 - res.lastIndexOf(separator);
                        }
                        lastSlash = i;
                        dots = 0;
                        continue;
                    } else if (res.length === 2 || res.length === 1) {
                        res = "";
                        lastSegmentLength = 0;
                        lastSlash = i;
                        dots = 0;
                        continue;
                    }
                }
                if (allowAboveRoot) {
                    if (res.length > 0) res += `${separator}..`;
                    else res = "..";
                    lastSegmentLength = 2;
                }
            } else {
                if (res.length > 0) res += separator + path.slice(lastSlash + 1, i);
                else res = path.slice(lastSlash + 1, i);
                lastSegmentLength = i - lastSlash - 1;
            }
            lastSlash = i;
            dots = 0;
        } else if (code === 46 && dots !== -1) {
            ++dots;
        } else {
            dots = -1;
        }
    }
    return res;
}
function _format(sep, pathObject) {
    const dir = pathObject.dir || pathObject.root;
    const base = pathObject.base || (pathObject.name || "") + (pathObject.ext || "");
    if (!dir) return base;
    if (dir === pathObject.root) return dir + base;
    return dir + sep + base;
}
const WHITESPACE_ENCODINGS = {
    "\u0009": "%09",
    "\u000A": "%0A",
    "\u000B": "%0B",
    "\u000C": "%0C",
    "\u000D": "%0D",
    "\u0020": "%20"
};
function encodeWhitespace(string) {
    return string.replaceAll(/[\s]/g, (c)=>{
        return WHITESPACE_ENCODINGS[c] ?? c;
    });
}
const sep = "\\";
const delimiter = ";";
function resolve(...pathSegments) {
    let resolvedDevice = "";
    let resolvedTail = "";
    let resolvedAbsolute = false;
    for(let i = pathSegments.length - 1; i >= -1; i--){
        let path;
        const { Deno: Deno1  } = globalThis;
        if (i >= 0) {
            path = pathSegments[i];
        } else if (!resolvedDevice) {
            if (typeof Deno1?.cwd !== "function") {
                throw new TypeError("Resolved a drive-letter-less path without a CWD.");
            }
            path = Deno1.cwd();
        } else {
            if (typeof Deno1?.env?.get !== "function" || typeof Deno1?.cwd !== "function") {
                throw new TypeError("Resolved a relative path without a CWD.");
            }
            path = Deno1.cwd();
            if (path === undefined || path.slice(0, 3).toLowerCase() !== `${resolvedDevice.toLowerCase()}\\`) {
                path = `${resolvedDevice}\\`;
            }
        }
        assertPath(path);
        const len = path.length;
        if (len === 0) continue;
        let rootEnd = 0;
        let device = "";
        let isAbsolute = false;
        const code = path.charCodeAt(0);
        if (len > 1) {
            if (isPathSeparator(code)) {
                isAbsolute = true;
                if (isPathSeparator(path.charCodeAt(1))) {
                    let j = 2;
                    let last = j;
                    for(; j < len; ++j){
                        if (isPathSeparator(path.charCodeAt(j))) break;
                    }
                    if (j < len && j !== last) {
                        const firstPart = path.slice(last, j);
                        last = j;
                        for(; j < len; ++j){
                            if (!isPathSeparator(path.charCodeAt(j))) break;
                        }
                        if (j < len && j !== last) {
                            last = j;
                            for(; j < len; ++j){
                                if (isPathSeparator(path.charCodeAt(j))) break;
                            }
                            if (j === len) {
                                device = `\\\\${firstPart}\\${path.slice(last)}`;
                                rootEnd = j;
                            } else if (j !== last) {
                                device = `\\\\${firstPart}\\${path.slice(last, j)}`;
                                rootEnd = j;
                            }
                        }
                    }
                } else {
                    rootEnd = 1;
                }
            } else if (isWindowsDeviceRoot(code)) {
                if (path.charCodeAt(1) === 58) {
                    device = path.slice(0, 2);
                    rootEnd = 2;
                    if (len > 2) {
                        if (isPathSeparator(path.charCodeAt(2))) {
                            isAbsolute = true;
                            rootEnd = 3;
                        }
                    }
                }
            }
        } else if (isPathSeparator(code)) {
            rootEnd = 1;
            isAbsolute = true;
        }
        if (device.length > 0 && resolvedDevice.length > 0 && device.toLowerCase() !== resolvedDevice.toLowerCase()) {
            continue;
        }
        if (resolvedDevice.length === 0 && device.length > 0) {
            resolvedDevice = device;
        }
        if (!resolvedAbsolute) {
            resolvedTail = `${path.slice(rootEnd)}\\${resolvedTail}`;
            resolvedAbsolute = isAbsolute;
        }
        if (resolvedAbsolute && resolvedDevice.length > 0) break;
    }
    resolvedTail = normalizeString(resolvedTail, !resolvedAbsolute, "\\", isPathSeparator);
    return resolvedDevice + (resolvedAbsolute ? "\\" : "") + resolvedTail || ".";
}
function normalize(path) {
    assertPath(path);
    const len = path.length;
    if (len === 0) return ".";
    let rootEnd = 0;
    let device;
    let isAbsolute = false;
    const code = path.charCodeAt(0);
    if (len > 1) {
        if (isPathSeparator(code)) {
            isAbsolute = true;
            if (isPathSeparator(path.charCodeAt(1))) {
                let j = 2;
                let last = j;
                for(; j < len; ++j){
                    if (isPathSeparator(path.charCodeAt(j))) break;
                }
                if (j < len && j !== last) {
                    const firstPart = path.slice(last, j);
                    last = j;
                    for(; j < len; ++j){
                        if (!isPathSeparator(path.charCodeAt(j))) break;
                    }
                    if (j < len && j !== last) {
                        last = j;
                        for(; j < len; ++j){
                            if (isPathSeparator(path.charCodeAt(j))) break;
                        }
                        if (j === len) {
                            return `\\\\${firstPart}\\${path.slice(last)}\\`;
                        } else if (j !== last) {
                            device = `\\\\${firstPart}\\${path.slice(last, j)}`;
                            rootEnd = j;
                        }
                    }
                }
            } else {
                rootEnd = 1;
            }
        } else if (isWindowsDeviceRoot(code)) {
            if (path.charCodeAt(1) === 58) {
                device = path.slice(0, 2);
                rootEnd = 2;
                if (len > 2) {
                    if (isPathSeparator(path.charCodeAt(2))) {
                        isAbsolute = true;
                        rootEnd = 3;
                    }
                }
            }
        }
    } else if (isPathSeparator(code)) {
        return "\\";
    }
    let tail;
    if (rootEnd < len) {
        tail = normalizeString(path.slice(rootEnd), !isAbsolute, "\\", isPathSeparator);
    } else {
        tail = "";
    }
    if (tail.length === 0 && !isAbsolute) tail = ".";
    if (tail.length > 0 && isPathSeparator(path.charCodeAt(len - 1))) {
        tail += "\\";
    }
    if (device === undefined) {
        if (isAbsolute) {
            if (tail.length > 0) return `\\${tail}`;
            else return "\\";
        } else if (tail.length > 0) {
            return tail;
        } else {
            return "";
        }
    } else if (isAbsolute) {
        if (tail.length > 0) return `${device}\\${tail}`;
        else return `${device}\\`;
    } else if (tail.length > 0) {
        return device + tail;
    } else {
        return device;
    }
}
function isAbsolute(path) {
    assertPath(path);
    const len = path.length;
    if (len === 0) return false;
    const code = path.charCodeAt(0);
    if (isPathSeparator(code)) {
        return true;
    } else if (isWindowsDeviceRoot(code)) {
        if (len > 2 && path.charCodeAt(1) === 58) {
            if (isPathSeparator(path.charCodeAt(2))) return true;
        }
    }
    return false;
}
function join(...paths) {
    const pathsCount = paths.length;
    if (pathsCount === 0) return ".";
    let joined;
    let firstPart = null;
    for(let i = 0; i < pathsCount; ++i){
        const path = paths[i];
        assertPath(path);
        if (path.length > 0) {
            if (joined === undefined) joined = firstPart = path;
            else joined += `\\${path}`;
        }
    }
    if (joined === undefined) return ".";
    let needsReplace = true;
    let slashCount = 0;
    assert(firstPart != null);
    if (isPathSeparator(firstPart.charCodeAt(0))) {
        ++slashCount;
        const firstLen = firstPart.length;
        if (firstLen > 1) {
            if (isPathSeparator(firstPart.charCodeAt(1))) {
                ++slashCount;
                if (firstLen > 2) {
                    if (isPathSeparator(firstPart.charCodeAt(2))) ++slashCount;
                    else {
                        needsReplace = false;
                    }
                }
            }
        }
    }
    if (needsReplace) {
        for(; slashCount < joined.length; ++slashCount){
            if (!isPathSeparator(joined.charCodeAt(slashCount))) break;
        }
        if (slashCount >= 2) joined = `\\${joined.slice(slashCount)}`;
    }
    return normalize(joined);
}
function relative(from, to) {
    assertPath(from);
    assertPath(to);
    if (from === to) return "";
    const fromOrig = resolve(from);
    const toOrig = resolve(to);
    if (fromOrig === toOrig) return "";
    from = fromOrig.toLowerCase();
    to = toOrig.toLowerCase();
    if (from === to) return "";
    let fromStart = 0;
    let fromEnd = from.length;
    for(; fromStart < fromEnd; ++fromStart){
        if (from.charCodeAt(fromStart) !== 92) break;
    }
    for(; fromEnd - 1 > fromStart; --fromEnd){
        if (from.charCodeAt(fromEnd - 1) !== 92) break;
    }
    const fromLen = fromEnd - fromStart;
    let toStart = 0;
    let toEnd = to.length;
    for(; toStart < toEnd; ++toStart){
        if (to.charCodeAt(toStart) !== 92) break;
    }
    for(; toEnd - 1 > toStart; --toEnd){
        if (to.charCodeAt(toEnd - 1) !== 92) break;
    }
    const toLen = toEnd - toStart;
    const length = fromLen < toLen ? fromLen : toLen;
    let lastCommonSep = -1;
    let i = 0;
    for(; i <= length; ++i){
        if (i === length) {
            if (toLen > length) {
                if (to.charCodeAt(toStart + i) === 92) {
                    return toOrig.slice(toStart + i + 1);
                } else if (i === 2) {
                    return toOrig.slice(toStart + i);
                }
            }
            if (fromLen > length) {
                if (from.charCodeAt(fromStart + i) === 92) {
                    lastCommonSep = i;
                } else if (i === 2) {
                    lastCommonSep = 3;
                }
            }
            break;
        }
        const fromCode = from.charCodeAt(fromStart + i);
        const toCode = to.charCodeAt(toStart + i);
        if (fromCode !== toCode) break;
        else if (fromCode === 92) lastCommonSep = i;
    }
    if (i !== length && lastCommonSep === -1) {
        return toOrig;
    }
    let out = "";
    if (lastCommonSep === -1) lastCommonSep = 0;
    for(i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i){
        if (i === fromEnd || from.charCodeAt(i) === 92) {
            if (out.length === 0) out += "..";
            else out += "\\..";
        }
    }
    if (out.length > 0) {
        return out + toOrig.slice(toStart + lastCommonSep, toEnd);
    } else {
        toStart += lastCommonSep;
        if (toOrig.charCodeAt(toStart) === 92) ++toStart;
        return toOrig.slice(toStart, toEnd);
    }
}
function toNamespacedPath(path) {
    if (typeof path !== "string") return path;
    if (path.length === 0) return "";
    const resolvedPath = resolve(path);
    if (resolvedPath.length >= 3) {
        if (resolvedPath.charCodeAt(0) === 92) {
            if (resolvedPath.charCodeAt(1) === 92) {
                const code = resolvedPath.charCodeAt(2);
                if (code !== 63 && code !== 46) {
                    return `\\\\?\\UNC\\${resolvedPath.slice(2)}`;
                }
            }
        } else if (isWindowsDeviceRoot(resolvedPath.charCodeAt(0))) {
            if (resolvedPath.charCodeAt(1) === 58 && resolvedPath.charCodeAt(2) === 92) {
                return `\\\\?\\${resolvedPath}`;
            }
        }
    }
    return path;
}
function dirname(path) {
    assertPath(path);
    const len = path.length;
    if (len === 0) return ".";
    let rootEnd = -1;
    let end = -1;
    let matchedSlash = true;
    let offset = 0;
    const code = path.charCodeAt(0);
    if (len > 1) {
        if (isPathSeparator(code)) {
            rootEnd = offset = 1;
            if (isPathSeparator(path.charCodeAt(1))) {
                let j = 2;
                let last = j;
                for(; j < len; ++j){
                    if (isPathSeparator(path.charCodeAt(j))) break;
                }
                if (j < len && j !== last) {
                    last = j;
                    for(; j < len; ++j){
                        if (!isPathSeparator(path.charCodeAt(j))) break;
                    }
                    if (j < len && j !== last) {
                        last = j;
                        for(; j < len; ++j){
                            if (isPathSeparator(path.charCodeAt(j))) break;
                        }
                        if (j === len) {
                            return path;
                        }
                        if (j !== last) {
                            rootEnd = offset = j + 1;
                        }
                    }
                }
            }
        } else if (isWindowsDeviceRoot(code)) {
            if (path.charCodeAt(1) === 58) {
                rootEnd = offset = 2;
                if (len > 2) {
                    if (isPathSeparator(path.charCodeAt(2))) rootEnd = offset = 3;
                }
            }
        }
    } else if (isPathSeparator(code)) {
        return path;
    }
    for(let i = len - 1; i >= offset; --i){
        if (isPathSeparator(path.charCodeAt(i))) {
            if (!matchedSlash) {
                end = i;
                break;
            }
        } else {
            matchedSlash = false;
        }
    }
    if (end === -1) {
        if (rootEnd === -1) return ".";
        else end = rootEnd;
    }
    return path.slice(0, end);
}
function basename(path, ext = "") {
    if (ext !== undefined && typeof ext !== "string") {
        throw new TypeError('"ext" argument must be a string');
    }
    assertPath(path);
    let start = 0;
    let end = -1;
    let matchedSlash = true;
    let i;
    if (path.length >= 2) {
        const drive = path.charCodeAt(0);
        if (isWindowsDeviceRoot(drive)) {
            if (path.charCodeAt(1) === 58) start = 2;
        }
    }
    if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
        if (ext.length === path.length && ext === path) return "";
        let extIdx = ext.length - 1;
        let firstNonSlashEnd = -1;
        for(i = path.length - 1; i >= start; --i){
            const code = path.charCodeAt(i);
            if (isPathSeparator(code)) {
                if (!matchedSlash) {
                    start = i + 1;
                    break;
                }
            } else {
                if (firstNonSlashEnd === -1) {
                    matchedSlash = false;
                    firstNonSlashEnd = i + 1;
                }
                if (extIdx >= 0) {
                    if (code === ext.charCodeAt(extIdx)) {
                        if (--extIdx === -1) {
                            end = i;
                        }
                    } else {
                        extIdx = -1;
                        end = firstNonSlashEnd;
                    }
                }
            }
        }
        if (start === end) end = firstNonSlashEnd;
        else if (end === -1) end = path.length;
        return path.slice(start, end);
    } else {
        for(i = path.length - 1; i >= start; --i){
            if (isPathSeparator(path.charCodeAt(i))) {
                if (!matchedSlash) {
                    start = i + 1;
                    break;
                }
            } else if (end === -1) {
                matchedSlash = false;
                end = i + 1;
            }
        }
        if (end === -1) return "";
        return path.slice(start, end);
    }
}
function extname(path) {
    assertPath(path);
    let start = 0;
    let startDot = -1;
    let startPart = 0;
    let end = -1;
    let matchedSlash = true;
    let preDotState = 0;
    if (path.length >= 2 && path.charCodeAt(1) === 58 && isWindowsDeviceRoot(path.charCodeAt(0))) {
        start = startPart = 2;
    }
    for(let i = path.length - 1; i >= start; --i){
        const code = path.charCodeAt(i);
        if (isPathSeparator(code)) {
            if (!matchedSlash) {
                startPart = i + 1;
                break;
            }
            continue;
        }
        if (end === -1) {
            matchedSlash = false;
            end = i + 1;
        }
        if (code === 46) {
            if (startDot === -1) startDot = i;
            else if (preDotState !== 1) preDotState = 1;
        } else if (startDot !== -1) {
            preDotState = -1;
        }
    }
    if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
        return "";
    }
    return path.slice(startDot, end);
}
function format(pathObject) {
    if (pathObject === null || typeof pathObject !== "object") {
        throw new TypeError(`The "pathObject" argument must be of type Object. Received type ${typeof pathObject}`);
    }
    return _format("\\", pathObject);
}
function parse1(path) {
    assertPath(path);
    const ret = {
        root: "",
        dir: "",
        base: "",
        ext: "",
        name: ""
    };
    const len = path.length;
    if (len === 0) return ret;
    let rootEnd = 0;
    let code = path.charCodeAt(0);
    if (len > 1) {
        if (isPathSeparator(code)) {
            rootEnd = 1;
            if (isPathSeparator(path.charCodeAt(1))) {
                let j = 2;
                let last = j;
                for(; j < len; ++j){
                    if (isPathSeparator(path.charCodeAt(j))) break;
                }
                if (j < len && j !== last) {
                    last = j;
                    for(; j < len; ++j){
                        if (!isPathSeparator(path.charCodeAt(j))) break;
                    }
                    if (j < len && j !== last) {
                        last = j;
                        for(; j < len; ++j){
                            if (isPathSeparator(path.charCodeAt(j))) break;
                        }
                        if (j === len) {
                            rootEnd = j;
                        } else if (j !== last) {
                            rootEnd = j + 1;
                        }
                    }
                }
            }
        } else if (isWindowsDeviceRoot(code)) {
            if (path.charCodeAt(1) === 58) {
                rootEnd = 2;
                if (len > 2) {
                    if (isPathSeparator(path.charCodeAt(2))) {
                        if (len === 3) {
                            ret.root = ret.dir = path;
                            return ret;
                        }
                        rootEnd = 3;
                    }
                } else {
                    ret.root = ret.dir = path;
                    return ret;
                }
            }
        }
    } else if (isPathSeparator(code)) {
        ret.root = ret.dir = path;
        return ret;
    }
    if (rootEnd > 0) ret.root = path.slice(0, rootEnd);
    let startDot = -1;
    let startPart = rootEnd;
    let end = -1;
    let matchedSlash = true;
    let i = path.length - 1;
    let preDotState = 0;
    for(; i >= rootEnd; --i){
        code = path.charCodeAt(i);
        if (isPathSeparator(code)) {
            if (!matchedSlash) {
                startPart = i + 1;
                break;
            }
            continue;
        }
        if (end === -1) {
            matchedSlash = false;
            end = i + 1;
        }
        if (code === 46) {
            if (startDot === -1) startDot = i;
            else if (preDotState !== 1) preDotState = 1;
        } else if (startDot !== -1) {
            preDotState = -1;
        }
    }
    if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
        if (end !== -1) {
            ret.base = ret.name = path.slice(startPart, end);
        }
    } else {
        ret.name = path.slice(startPart, startDot);
        ret.base = path.slice(startPart, end);
        ret.ext = path.slice(startDot, end);
    }
    if (startPart > 0 && startPart !== rootEnd) {
        ret.dir = path.slice(0, startPart - 1);
    } else ret.dir = ret.root;
    return ret;
}
function fromFileUrl(url) {
    url = url instanceof URL ? url : new URL(url);
    if (url.protocol != "file:") {
        throw new TypeError("Must be a file URL.");
    }
    let path = decodeURIComponent(url.pathname.replace(/\//g, "\\").replace(/%(?![0-9A-Fa-f]{2})/g, "%25")).replace(/^\\*([A-Za-z]:)(\\|$)/, "$1\\");
    if (url.hostname != "") {
        path = `\\\\${url.hostname}${path}`;
    }
    return path;
}
function toFileUrl(path) {
    if (!isAbsolute(path)) {
        throw new TypeError("Must be an absolute path.");
    }
    const [, hostname, pathname] = path.match(/^(?:[/\\]{2}([^/\\]+)(?=[/\\](?:[^/\\]|$)))?(.*)/);
    const url = new URL("file:///");
    url.pathname = encodeWhitespace(pathname.replace(/%/g, "%25"));
    if (hostname != null && hostname != "localhost") {
        url.hostname = hostname;
        if (!url.hostname) {
            throw new TypeError("Invalid hostname.");
        }
    }
    return url;
}
const mod8 = {
    sep: sep,
    delimiter: delimiter,
    resolve: resolve,
    normalize: normalize,
    isAbsolute: isAbsolute,
    join: join,
    relative: relative,
    toNamespacedPath: toNamespacedPath,
    dirname: dirname,
    basename: basename,
    extname: extname,
    format: format,
    parse: parse1,
    fromFileUrl: fromFileUrl,
    toFileUrl: toFileUrl
};
const sep1 = "/";
const delimiter1 = ":";
function resolve1(...pathSegments) {
    let resolvedPath = "";
    let resolvedAbsolute = false;
    for(let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--){
        let path;
        if (i >= 0) path = pathSegments[i];
        else {
            const { Deno: Deno1  } = globalThis;
            if (typeof Deno1?.cwd !== "function") {
                throw new TypeError("Resolved a relative path without a CWD.");
            }
            path = Deno1.cwd();
        }
        assertPath(path);
        if (path.length === 0) {
            continue;
        }
        resolvedPath = `${path}/${resolvedPath}`;
        resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH;
    }
    resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute, "/", isPosixPathSeparator);
    if (resolvedAbsolute) {
        if (resolvedPath.length > 0) return `/${resolvedPath}`;
        else return "/";
    } else if (resolvedPath.length > 0) return resolvedPath;
    else return ".";
}
function normalize1(path) {
    assertPath(path);
    if (path.length === 0) return ".";
    const isAbsolute = path.charCodeAt(0) === 47;
    const trailingSeparator = path.charCodeAt(path.length - 1) === 47;
    path = normalizeString(path, !isAbsolute, "/", isPosixPathSeparator);
    if (path.length === 0 && !isAbsolute) path = ".";
    if (path.length > 0 && trailingSeparator) path += "/";
    if (isAbsolute) return `/${path}`;
    return path;
}
function isAbsolute1(path) {
    assertPath(path);
    return path.length > 0 && path.charCodeAt(0) === 47;
}
function join1(...paths) {
    if (paths.length === 0) return ".";
    let joined;
    for(let i = 0, len = paths.length; i < len; ++i){
        const path = paths[i];
        assertPath(path);
        if (path.length > 0) {
            if (!joined) joined = path;
            else joined += `/${path}`;
        }
    }
    if (!joined) return ".";
    return normalize1(joined);
}
function relative1(from, to) {
    assertPath(from);
    assertPath(to);
    if (from === to) return "";
    from = resolve1(from);
    to = resolve1(to);
    if (from === to) return "";
    let fromStart = 1;
    const fromEnd = from.length;
    for(; fromStart < fromEnd; ++fromStart){
        if (from.charCodeAt(fromStart) !== 47) break;
    }
    const fromLen = fromEnd - fromStart;
    let toStart = 1;
    const toEnd = to.length;
    for(; toStart < toEnd; ++toStart){
        if (to.charCodeAt(toStart) !== 47) break;
    }
    const toLen = toEnd - toStart;
    const length = fromLen < toLen ? fromLen : toLen;
    let lastCommonSep = -1;
    let i = 0;
    for(; i <= length; ++i){
        if (i === length) {
            if (toLen > length) {
                if (to.charCodeAt(toStart + i) === 47) {
                    return to.slice(toStart + i + 1);
                } else if (i === 0) {
                    return to.slice(toStart + i);
                }
            } else if (fromLen > length) {
                if (from.charCodeAt(fromStart + i) === 47) {
                    lastCommonSep = i;
                } else if (i === 0) {
                    lastCommonSep = 0;
                }
            }
            break;
        }
        const fromCode = from.charCodeAt(fromStart + i);
        const toCode = to.charCodeAt(toStart + i);
        if (fromCode !== toCode) break;
        else if (fromCode === 47) lastCommonSep = i;
    }
    let out = "";
    for(i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i){
        if (i === fromEnd || from.charCodeAt(i) === 47) {
            if (out.length === 0) out += "..";
            else out += "/..";
        }
    }
    if (out.length > 0) return out + to.slice(toStart + lastCommonSep);
    else {
        toStart += lastCommonSep;
        if (to.charCodeAt(toStart) === 47) ++toStart;
        return to.slice(toStart);
    }
}
function toNamespacedPath1(path) {
    return path;
}
function dirname1(path) {
    assertPath(path);
    if (path.length === 0) return ".";
    const hasRoot = path.charCodeAt(0) === 47;
    let end = -1;
    let matchedSlash = true;
    for(let i = path.length - 1; i >= 1; --i){
        if (path.charCodeAt(i) === 47) {
            if (!matchedSlash) {
                end = i;
                break;
            }
        } else {
            matchedSlash = false;
        }
    }
    if (end === -1) return hasRoot ? "/" : ".";
    if (hasRoot && end === 1) return "//";
    return path.slice(0, end);
}
function basename1(path, ext = "") {
    if (ext !== undefined && typeof ext !== "string") {
        throw new TypeError('"ext" argument must be a string');
    }
    assertPath(path);
    let start = 0;
    let end = -1;
    let matchedSlash = true;
    let i;
    if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
        if (ext.length === path.length && ext === path) return "";
        let extIdx = ext.length - 1;
        let firstNonSlashEnd = -1;
        for(i = path.length - 1; i >= 0; --i){
            const code = path.charCodeAt(i);
            if (code === 47) {
                if (!matchedSlash) {
                    start = i + 1;
                    break;
                }
            } else {
                if (firstNonSlashEnd === -1) {
                    matchedSlash = false;
                    firstNonSlashEnd = i + 1;
                }
                if (extIdx >= 0) {
                    if (code === ext.charCodeAt(extIdx)) {
                        if (--extIdx === -1) {
                            end = i;
                        }
                    } else {
                        extIdx = -1;
                        end = firstNonSlashEnd;
                    }
                }
            }
        }
        if (start === end) end = firstNonSlashEnd;
        else if (end === -1) end = path.length;
        return path.slice(start, end);
    } else {
        for(i = path.length - 1; i >= 0; --i){
            if (path.charCodeAt(i) === 47) {
                if (!matchedSlash) {
                    start = i + 1;
                    break;
                }
            } else if (end === -1) {
                matchedSlash = false;
                end = i + 1;
            }
        }
        if (end === -1) return "";
        return path.slice(start, end);
    }
}
function extname1(path) {
    assertPath(path);
    let startDot = -1;
    let startPart = 0;
    let end = -1;
    let matchedSlash = true;
    let preDotState = 0;
    for(let i = path.length - 1; i >= 0; --i){
        const code = path.charCodeAt(i);
        if (code === 47) {
            if (!matchedSlash) {
                startPart = i + 1;
                break;
            }
            continue;
        }
        if (end === -1) {
            matchedSlash = false;
            end = i + 1;
        }
        if (code === 46) {
            if (startDot === -1) startDot = i;
            else if (preDotState !== 1) preDotState = 1;
        } else if (startDot !== -1) {
            preDotState = -1;
        }
    }
    if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
        return "";
    }
    return path.slice(startDot, end);
}
function format1(pathObject) {
    if (pathObject === null || typeof pathObject !== "object") {
        throw new TypeError(`The "pathObject" argument must be of type Object. Received type ${typeof pathObject}`);
    }
    return _format("/", pathObject);
}
function parse2(path) {
    assertPath(path);
    const ret = {
        root: "",
        dir: "",
        base: "",
        ext: "",
        name: ""
    };
    if (path.length === 0) return ret;
    const isAbsolute = path.charCodeAt(0) === 47;
    let start;
    if (isAbsolute) {
        ret.root = "/";
        start = 1;
    } else {
        start = 0;
    }
    let startDot = -1;
    let startPart = 0;
    let end = -1;
    let matchedSlash = true;
    let i = path.length - 1;
    let preDotState = 0;
    for(; i >= start; --i){
        const code = path.charCodeAt(i);
        if (code === 47) {
            if (!matchedSlash) {
                startPart = i + 1;
                break;
            }
            continue;
        }
        if (end === -1) {
            matchedSlash = false;
            end = i + 1;
        }
        if (code === 46) {
            if (startDot === -1) startDot = i;
            else if (preDotState !== 1) preDotState = 1;
        } else if (startDot !== -1) {
            preDotState = -1;
        }
    }
    if (startDot === -1 || end === -1 || preDotState === 0 || preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
        if (end !== -1) {
            if (startPart === 0 && isAbsolute) {
                ret.base = ret.name = path.slice(1, end);
            } else {
                ret.base = ret.name = path.slice(startPart, end);
            }
        }
    } else {
        if (startPart === 0 && isAbsolute) {
            ret.name = path.slice(1, startDot);
            ret.base = path.slice(1, end);
        } else {
            ret.name = path.slice(startPart, startDot);
            ret.base = path.slice(startPart, end);
        }
        ret.ext = path.slice(startDot, end);
    }
    if (startPart > 0) ret.dir = path.slice(0, startPart - 1);
    else if (isAbsolute) ret.dir = "/";
    return ret;
}
function fromFileUrl1(url) {
    url = url instanceof URL ? url : new URL(url);
    if (url.protocol != "file:") {
        throw new TypeError("Must be a file URL.");
    }
    return decodeURIComponent(url.pathname.replace(/%(?![0-9A-Fa-f]{2})/g, "%25"));
}
function toFileUrl1(path) {
    if (!isAbsolute1(path)) {
        throw new TypeError("Must be an absolute path.");
    }
    const url = new URL("file:///");
    url.pathname = encodeWhitespace(path.replace(/%/g, "%25").replace(/\\/g, "%5C"));
    return url;
}
const mod9 = {
    sep: sep1,
    delimiter: delimiter1,
    resolve: resolve1,
    normalize: normalize1,
    isAbsolute: isAbsolute1,
    join: join1,
    relative: relative1,
    toNamespacedPath: toNamespacedPath1,
    dirname: dirname1,
    basename: basename1,
    extname: extname1,
    format: format1,
    parse: parse2,
    fromFileUrl: fromFileUrl1,
    toFileUrl: toFileUrl1
};
const SEP = isWindows ? "\\" : "/";
const SEP_PATTERN = isWindows ? /[\\/]+/ : /\/+/;
function common(paths, sep = SEP) {
    const [first = "", ...remaining] = paths;
    if (first === "" || remaining.length === 0) {
        return first.substring(0, first.lastIndexOf(sep) + 1);
    }
    const parts = first.split(sep);
    let endOfPrefix = parts.length;
    for (const path of remaining){
        const compare = path.split(sep);
        for(let i = 0; i < endOfPrefix; i++){
            if (compare[i] !== parts[i]) {
                endOfPrefix = i;
            }
        }
        if (endOfPrefix === 0) {
            return "";
        }
    }
    const prefix = parts.slice(0, endOfPrefix).join(sep);
    return prefix.endsWith(sep) ? prefix : `${prefix}${sep}`;
}
const path = isWindows ? mod8 : mod9;
const { join: join2 , normalize: normalize2  } = path;
const regExpEscapeChars = [
    "!",
    "$",
    "(",
    ")",
    "*",
    "+",
    ".",
    "=",
    "?",
    "[",
    "\\",
    "^",
    "{",
    "|"
];
const rangeEscapeChars = [
    "-",
    "\\",
    "]"
];
function globToRegExp(glob, { extended =true , globstar: globstarOption = true , os =osType , caseInsensitive =false  } = {}) {
    if (glob == "") {
        return /(?!)/;
    }
    const sep = os == "windows" ? "(?:\\\\|/)+" : "/+";
    const sepMaybe = os == "windows" ? "(?:\\\\|/)*" : "/*";
    const seps = os == "windows" ? [
        "\\",
        "/"
    ] : [
        "/"
    ];
    const globstar = os == "windows" ? "(?:[^\\\\/]*(?:\\\\|/|$)+)*" : "(?:[^/]*(?:/|$)+)*";
    const wildcard = os == "windows" ? "[^\\\\/]*" : "[^/]*";
    const escapePrefix = os == "windows" ? "`" : "\\";
    let newLength = glob.length;
    for(; newLength > 1 && seps.includes(glob[newLength - 1]); newLength--);
    glob = glob.slice(0, newLength);
    let regExpString = "";
    for(let j = 0; j < glob.length;){
        let segment = "";
        const groupStack = [];
        let inRange = false;
        let inEscape = false;
        let endsWithSep = false;
        let i = j;
        for(; i < glob.length && !seps.includes(glob[i]); i++){
            if (inEscape) {
                inEscape = false;
                const escapeChars = inRange ? rangeEscapeChars : regExpEscapeChars;
                segment += escapeChars.includes(glob[i]) ? `\\${glob[i]}` : glob[i];
                continue;
            }
            if (glob[i] == escapePrefix) {
                inEscape = true;
                continue;
            }
            if (glob[i] == "[") {
                if (!inRange) {
                    inRange = true;
                    segment += "[";
                    if (glob[i + 1] == "!") {
                        i++;
                        segment += "^";
                    } else if (glob[i + 1] == "^") {
                        i++;
                        segment += "\\^";
                    }
                    continue;
                } else if (glob[i + 1] == ":") {
                    let k = i + 1;
                    let value = "";
                    while(glob[k + 1] != null && glob[k + 1] != ":"){
                        value += glob[k + 1];
                        k++;
                    }
                    if (glob[k + 1] == ":" && glob[k + 2] == "]") {
                        i = k + 2;
                        if (value == "alnum") segment += "\\dA-Za-z";
                        else if (value == "alpha") segment += "A-Za-z";
                        else if (value == "ascii") segment += "\x00-\x7F";
                        else if (value == "blank") segment += "\t ";
                        else if (value == "cntrl") segment += "\x00-\x1F\x7F";
                        else if (value == "digit") segment += "\\d";
                        else if (value == "graph") segment += "\x21-\x7E";
                        else if (value == "lower") segment += "a-z";
                        else if (value == "print") segment += "\x20-\x7E";
                        else if (value == "punct") {
                            segment += "!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_‘{|}~";
                        } else if (value == "space") segment += "\\s\v";
                        else if (value == "upper") segment += "A-Z";
                        else if (value == "word") segment += "\\w";
                        else if (value == "xdigit") segment += "\\dA-Fa-f";
                        continue;
                    }
                }
            }
            if (glob[i] == "]" && inRange) {
                inRange = false;
                segment += "]";
                continue;
            }
            if (inRange) {
                if (glob[i] == "\\") {
                    segment += `\\\\`;
                } else {
                    segment += glob[i];
                }
                continue;
            }
            if (glob[i] == ")" && groupStack.length > 0 && groupStack[groupStack.length - 1] != "BRACE") {
                segment += ")";
                const type = groupStack.pop();
                if (type == "!") {
                    segment += wildcard;
                } else if (type != "@") {
                    segment += type;
                }
                continue;
            }
            if (glob[i] == "|" && groupStack.length > 0 && groupStack[groupStack.length - 1] != "BRACE") {
                segment += "|";
                continue;
            }
            if (glob[i] == "+" && extended && glob[i + 1] == "(") {
                i++;
                groupStack.push("+");
                segment += "(?:";
                continue;
            }
            if (glob[i] == "@" && extended && glob[i + 1] == "(") {
                i++;
                groupStack.push("@");
                segment += "(?:";
                continue;
            }
            if (glob[i] == "?") {
                if (extended && glob[i + 1] == "(") {
                    i++;
                    groupStack.push("?");
                    segment += "(?:";
                } else {
                    segment += ".";
                }
                continue;
            }
            if (glob[i] == "!" && extended && glob[i + 1] == "(") {
                i++;
                groupStack.push("!");
                segment += "(?!";
                continue;
            }
            if (glob[i] == "{") {
                groupStack.push("BRACE");
                segment += "(?:";
                continue;
            }
            if (glob[i] == "}" && groupStack[groupStack.length - 1] == "BRACE") {
                groupStack.pop();
                segment += ")";
                continue;
            }
            if (glob[i] == "," && groupStack[groupStack.length - 1] == "BRACE") {
                segment += "|";
                continue;
            }
            if (glob[i] == "*") {
                if (extended && glob[i + 1] == "(") {
                    i++;
                    groupStack.push("*");
                    segment += "(?:";
                } else {
                    const prevChar = glob[i - 1];
                    let numStars = 1;
                    while(glob[i + 1] == "*"){
                        i++;
                        numStars++;
                    }
                    const nextChar = glob[i + 1];
                    if (globstarOption && numStars == 2 && [
                        ...seps,
                        undefined
                    ].includes(prevChar) && [
                        ...seps,
                        undefined
                    ].includes(nextChar)) {
                        segment += globstar;
                        endsWithSep = true;
                    } else {
                        segment += wildcard;
                    }
                }
                continue;
            }
            segment += regExpEscapeChars.includes(glob[i]) ? `\\${glob[i]}` : glob[i];
        }
        if (groupStack.length > 0 || inRange || inEscape) {
            segment = "";
            for (const c of glob.slice(j, i)){
                segment += regExpEscapeChars.includes(c) ? `\\${c}` : c;
                endsWithSep = false;
            }
        }
        regExpString += segment;
        if (!endsWithSep) {
            regExpString += i < glob.length ? sep : sepMaybe;
            endsWithSep = true;
        }
        while(seps.includes(glob[i]))i++;
        if (!(i > j)) {
            throw new Error("Assertion failure: i > j (potential infinite loop)");
        }
        j = i;
    }
    regExpString = `^${regExpString}$`;
    return new RegExp(regExpString, caseInsensitive ? "i" : "");
}
function isGlob(str) {
    const chars = {
        "{": "}",
        "(": ")",
        "[": "]"
    };
    const regex = /\\(.)|(^!|\*|\?|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/;
    if (str === "") {
        return false;
    }
    let match;
    while(match = regex.exec(str)){
        if (match[2]) return true;
        let idx = match.index + match[0].length;
        const open = match[1];
        const close = open ? chars[open] : null;
        if (open && close) {
            const n = str.indexOf(close, idx);
            if (n !== -1) {
                idx = n + 1;
            }
        }
        str = str.slice(idx);
    }
    return false;
}
function normalizeGlob(glob, { globstar =false  } = {}) {
    if (glob.match(/\0/g)) {
        throw new Error(`Glob contains invalid characters: "${glob}"`);
    }
    if (!globstar) {
        return normalize2(glob);
    }
    const s = SEP_PATTERN.source;
    const badParentPattern = new RegExp(`(?<=(${s}|^)\\*\\*${s})\\.\\.(?=${s}|$)`, "g");
    return normalize2(glob.replace(badParentPattern, "\0")).replace(/\0/g, "..");
}
function joinGlobs(globs, { extended =true , globstar =false  } = {}) {
    if (!globstar || globs.length == 0) {
        return join2(...globs);
    }
    if (globs.length === 0) return ".";
    let joined;
    for (const glob of globs){
        const path = glob;
        if (path.length > 0) {
            if (!joined) joined = path;
            else joined += `${SEP}${path}`;
        }
    }
    if (!joined) return ".";
    return normalizeGlob(joined, {
        extended,
        globstar
    });
}
const path1 = isWindows ? mod8 : mod9;
const { basename: basename2 , delimiter: delimiter2 , dirname: dirname2 , extname: extname2 , format: format2 , fromFileUrl: fromFileUrl2 , isAbsolute: isAbsolute2 , join: join3 , normalize: normalize3 , parse: parse3 , relative: relative2 , resolve: resolve2 , sep: sep2 , toFileUrl: toFileUrl2 , toNamespacedPath: toNamespacedPath2  } = path1;
const mod10 = {
    SEP: SEP,
    SEP_PATTERN: SEP_PATTERN,
    win32: mod8,
    posix: mod9,
    basename: basename2,
    delimiter: delimiter2,
    dirname: dirname2,
    extname: extname2,
    format: format2,
    fromFileUrl: fromFileUrl2,
    isAbsolute: isAbsolute2,
    join: join3,
    normalize: normalize3,
    parse: parse3,
    relative: relative2,
    resolve: resolve2,
    sep: sep2,
    toFileUrl: toFileUrl2,
    toNamespacedPath: toNamespacedPath2,
    common,
    globToRegExp,
    isGlob,
    normalizeGlob,
    joinGlobs
};
const { Deno: Deno1  } = globalThis;
const noColor = typeof Deno1?.noColor === "boolean" ? Deno1.noColor : true;
let enabled = !noColor;
function code(open, close) {
    return {
        open: `\x1b[${open.join(";")}m`,
        close: `\x1b[${close}m`,
        regexp: new RegExp(`\\x1b\\[${close}m`, "g")
    };
}
function run(str, code) {
    return enabled ? `${code.open}${str.replace(code.regexp, code.open)}${code.close}` : str;
}
function bgGreen(str) {
    return run(str, code([
        42
    ], 49));
}
function bgWhite(str) {
    return run(str, code([
        47
    ], 49));
}
new RegExp([
    "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
    "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"
].join("|"), "g");
function writeAllSync(w, arr) {
    let nwritten = 0;
    while(nwritten < arr.length){
        nwritten += w.writeSync(arr.subarray(nwritten));
    }
}
const isTTY = Deno.stdout && Deno.isatty(Deno.stdout.rid);
const isWindow = Deno.build.os === "windows";
class MultiProgressBar {
    width;
    complete;
    incomplete;
    clear;
    interval;
    display;
    #end = false;
    #startIndex = 0;
    #lastRows = 0;
    #strs = [];
    lastStr = "";
    start = Date.now();
    lastRender = 0;
    encoder = new TextEncoder();
    constructor({ title ="" , width =50 , complete =bgGreen(" ") , incomplete =bgWhite(" ") , clear =false , interval , display  } = {}){
        if (title != "") {
            this.#strs.push(title);
            this.#startIndex = 1;
        }
        this.width = width;
        this.complete = complete;
        this.incomplete = incomplete;
        this.clear = clear;
        this.interval = interval ?? 16;
        this.display = display ?? ":bar :text :percent :time :completed/:total";
    }
    render(bars) {
        if (this.#end || !isTTY) return;
        const now = Date.now();
        const ms = now - this.lastRender;
        this.lastRender = now;
        const time = ((now - this.start) / 1000).toFixed(1) + "s";
        let end = true;
        let index = this.#startIndex;
        for (const { completed , total =100 , text ="" , ...options } of bars){
            if (completed < 0) {
                throw new Error(`completed must greater than or equal to 0`);
            }
            if (!Number.isInteger(total)) throw new Error(`total must be 'number'`);
            if (completed > total && this.#strs[index] != undefined) continue;
            end = false;
            const percent = (completed / total * 100).toFixed(2) + "%";
            const eta = completed == 0 ? "-" : (completed >= total ? 0 : (total / completed - 1) * (now - this.start) / 1000).toFixed(1) + "s";
            let str = this.display.replace(":text", text).replace(":time", time).replace(":eta", eta).replace(":percent", percent).replace(":completed", completed + "").replace(":total", total + "");
            let availableSpace = Math.max(0, this.ttyColumns - str.replace(":bar", "").length);
            if (availableSpace && isWindow) availableSpace -= 1;
            const width = Math.min(this.width, availableSpace);
            const completeLength = Math.round(width * completed / total);
            const complete = new Array(completeLength).fill(options.complete ?? this.complete).join("");
            const incomplete = new Array(width - completeLength).fill(options.incomplete ?? this.incomplete).join("");
            str = str.replace(":bar", complete + incomplete);
            if (this.#strs[index] && str.length < this.#strs[index].length) {
                str += " ".repeat(this.#strs[index].length - str.length);
            }
            this.#strs[index++] = str;
        }
        if (ms < this.interval && end == false) return;
        const renderStr = this.#strs.join("\n");
        if (renderStr !== this.lastStr) {
            this.resetScreen();
            this.write(renderStr);
            this.lastStr = renderStr;
            this.#lastRows = this.#strs.length;
        }
        if (end) this.end();
    }
    end() {
        this.#end = true;
        if (this.clear) {
            this.resetScreen();
        } else {
            this.breakLine();
        }
        this.showCursor();
    }
    console(message) {
        this.resetScreen();
        this.write(`${message}`);
        this.breakLine();
        this.write(this.lastStr);
    }
    write(msg) {
        msg = `${msg}\x1b[?25l`;
        this.stdoutWrite(msg);
    }
    resetScreen() {
        if (this.#lastRows > 0) {
            this.stdoutWrite("\x1b[" + (this.#lastRows - 1) + "A\r\x1b[?0J");
        }
    }
    get ttyColumns() {
        return 100;
    }
    breakLine() {
        this.stdoutWrite("\r\n");
    }
    stdoutWrite(msg) {
        writeAllSync(Deno.stdout, this.encoder.encode(msg));
    }
    showCursor() {
        this.stdoutWrite("\x1b[?25h");
    }
}
const isTTY1 = Deno.stdout && Deno.isatty(Deno.stdout.rid);
const isWindow1 = Deno.build.os === "windows";
var Direction;
(function(Direction) {
    Direction[Direction["left"] = 0] = "left";
    Direction[Direction["right"] = 1] = "right";
    Direction[Direction["all"] = 2] = "all";
})(Direction || (Direction = {}));
class ProgressBar {
    title;
    total;
    width;
    complete;
    preciseBar;
    incomplete;
    clear;
    interval;
    display;
    isCompleted = false;
    lastStr = "";
    start = Date.now();
    lastRender = 0;
    encoder = new TextEncoder();
    constructor({ title ="" , total , width =50 , complete =bgGreen(" ") , preciseBar =[] , incomplete =bgWhite(" ") , clear =false , interval =16 , display  } = {}){
        this.title = title;
        this.total = total;
        this.width = width;
        this.complete = complete;
        this.preciseBar = preciseBar.concat(complete);
        this.incomplete = incomplete;
        this.clear = clear;
        this.interval = interval;
        this.display = display ?? ":title :percent :bar :time :completed/:total";
    }
    render(completed, options = {}) {
        if (this.isCompleted || !isTTY1) return;
        if (completed < 0) {
            throw new Error(`completed must greater than or equal to 0`);
        }
        const total = options.total ?? this.total ?? 100;
        const now = Date.now();
        const ms = now - this.lastRender;
        if (ms < this.interval && completed < total) return;
        this.lastRender = now;
        const time = ((now - this.start) / 1000).toFixed(1) + "s";
        const eta = completed == 0 ? "-" : (completed >= total ? 0 : (total / completed - 1) * (now - this.start) / 1000).toFixed(1) + "s";
        const percent = (completed / total * 100).toFixed(2) + "%";
        let str = this.display.replace(":title", options.title ?? this.title).replace(":time", time).replace(":eta", eta).replace(":percent", percent).replace(":completed", completed + "").replace(":total", total + "");
        let availableSpace = Math.max(0, this.ttyColumns - str.replace(":bar", "").length);
        if (availableSpace && isWindow1) availableSpace -= 1;
        const width = Math.min(this.width, availableSpace);
        const finished = completed >= total;
        const preciseBar = options.preciseBar ?? this.preciseBar;
        const precision = preciseBar.length > 1;
        const completeLength = width * completed / total;
        const roundedCompleteLength = Math.floor(completeLength);
        let precise = "";
        if (precision) {
            const preciseLength = completeLength - roundedCompleteLength;
            precise = finished ? "" : preciseBar[Math.floor(preciseBar.length * preciseLength)];
        }
        const complete = new Array(roundedCompleteLength).fill(options.complete ?? this.complete).join("");
        const incomplete = new Array(Math.max(width - roundedCompleteLength - (precision ? 1 : 0), 0)).fill(options.incomplete ?? this.incomplete).join("");
        str = str.replace(":bar", complete + precise + incomplete);
        if (str.length < this.lastStr.length) {
            str += " ".repeat(this.lastStr.length - str.length);
        }
        if (str !== this.lastStr) {
            this.write(str);
            this.lastStr = str;
        }
        if (finished) this.end();
    }
    end() {
        this.isCompleted = true;
        if (this.clear) {
            this.stdoutWrite("\r");
            this.clearLine();
        } else {
            this.breakLine();
        }
        this.showCursor();
    }
    console(message) {
        this.clearLine();
        this.write(`${message}`);
        this.breakLine();
        this.write(this.lastStr);
    }
    write(msg) {
        msg = `\r${msg}\x1b[?25l`;
        this.stdoutWrite(msg);
    }
    get ttyColumns() {
        return 100;
    }
    breakLine() {
        this.stdoutWrite("\r\n");
    }
    stdoutWrite(msg) {
        writeAllSync(Deno.stdout, this.encoder.encode(msg));
    }
    clearLine(direction = 2) {
        switch(direction){
            case 2:
                this.stdoutWrite("\x1b[2K");
                break;
            case 0:
                this.stdoutWrite("\x1b[1K");
                break;
            case 1:
                this.stdoutWrite("\x1b[0K");
                break;
        }
    }
    showCursor() {
        this.stdoutWrite("\x1b[?25h");
    }
}
class Semaphore {
    tasks = [];
    count;
    constructor(count){
        this.count = count;
    }
    schedule() {
        if (this.count > 0 && this.tasks.length > 0) {
            this.count--;
            const next = this.tasks.shift();
            if (next === undefined) {
                throw "Unexpected undefined value in tasks list";
            }
            next();
        }
    }
    get length() {
        return this.tasks.length;
    }
    acquire() {
        return new Promise((resolve)=>{
            const task = ()=>{
                let released = false;
                resolve(()=>{
                    if (!released) {
                        released = true;
                        this.count++;
                        this.schedule();
                    }
                });
            };
            this.tasks.push(task);
            queueMicrotask(this.schedule.bind(this));
        });
    }
    async use(fn) {
        const release = await this.acquire();
        try {
            const res = await fn();
            release();
            return res;
        } catch (err) {
            release();
            throw err;
        }
    }
}
class Mutex extends Semaphore {
    constructor(){
        super(1);
    }
}
const stdinLines = mod2.readLines(Deno.stdin);
async function readline({ skipEmpty =true  } = {}) {
    for await (const line of stdinLines){
        if (!skipEmpty || line !== "") {
            return line;
        }
    }
    throw new Error("EOF");
}
function urlBase64Encode(data) {
    return mod.encode(data).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
}
async function retry(f, times = 2) {
    let lastError;
    for(let i = 0; i < times; i++){
        try {
            return await f();
        } catch (e) {
            lastError = e;
        }
    }
    throw lastError;
}
async function showError(env, p) {
    try {
        return await p;
    } catch (e) {
        if (e instanceof APIError) {
            env.logger.error(`\n\nAPIError: ${e.message}`, "\nResponse: ", e.response, "\nBody: ", e.json);
        } else {
            env.logger.error(e);
        }
        throw e;
    }
}
function gameId(id) {
    const parsed = parseHistoryDetailId(id);
    if (parsed.type === "VsHistoryDetail") {
        const content = new TextEncoder().encode(`${parsed.timestamp}_${parsed.uuid}`);
        return mod6.v5.generate(BATTLE_NAMESPACE, content);
    } else if (parsed.type === "CoopHistoryDetail") {
        return mod6.v5.generate(COOP_NAMESPACE, mod.decode(id));
    } else {
        throw new Error("Unknown type");
    }
}
function s3siGameId(id) {
    const fullId = mod.decode(id);
    const tsUuid = fullId.slice(fullId.length - 52, fullId.length);
    return mod6.v5.generate(S3SI_NAMESPACE, tsUuid);
}
function s3sCoopGameId(id) {
    const fullId = mod.decode(id);
    const tsUuid = fullId.slice(fullId.length - 52, fullId.length);
    return mod6.v5.generate(COOP_NAMESPACE, tsUuid);
}
function parseHistoryDetailId(id) {
    const plainText = new TextDecoder().decode(mod.decode(id));
    const vsRE = /VsHistoryDetail-([a-z0-9-]+):(\w+):(\d{8}T\d{6})_([0-9a-f-]{36})/;
    const coopRE = /CoopHistoryDetail-([a-z0-9-]+):(\d{8}T\d{6})_([0-9a-f-]{36})/;
    if (vsRE.test(plainText)) {
        const [, uid, listType, timestamp, uuid] = plainText.match(vsRE);
        return {
            type: "VsHistoryDetail",
            uid,
            listType,
            timestamp,
            uuid
        };
    } else if (coopRE.test(plainText)) {
        const [, uid, timestamp, uuid] = plainText.match(coopRE);
        return {
            type: "CoopHistoryDetail",
            uid,
            timestamp,
            uuid
        };
    } else {
        throw new Error(`Invalid ID: ${plainText}`);
    }
}
const delay = (ms)=>new Promise((resolve)=>setTimeout(resolve, ms));
function b64Number(id) {
    const text = new TextDecoder().decode(mod.decode(id));
    const [_, num] = text.split("-");
    return parseInt(num);
}
function nonNullable(v) {
    return v !== null && v !== undefined;
}
function urlSimplify(url) {
    try {
        const { pathname  } = new URL(url);
        return {
            pathname
        };
    } catch (_e) {
        return url;
    }
}
const battleTime = (id)=>{
    const { timestamp  } = parseHistoryDetailId(id);
    const dateStr = timestamp.replace(/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})/, "$1-$2-$3T$4:$5:$6Z");
    return new Date(dateStr);
};
async function loginSteps({ newFetcher  }, step2) {
    const fetch = newFetcher();
    if (!step2) {
        const state = urlBase64Encode(random(36));
        const authCodeVerifier = urlBase64Encode(random(32));
        const authCvHash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(authCodeVerifier));
        const authCodeChallenge = urlBase64Encode(authCvHash);
        const body = {
            "state": state,
            "redirect_uri": "npf71b963c1b7b6d119://auth",
            "client_id": "71b963c1b7b6d119",
            "scope": "openid user user.birthday user.mii user.screenName",
            "response_type": "session_token_code",
            "session_token_code_challenge": authCodeChallenge,
            "session_token_code_challenge_method": "S256",
            "theme": "login_form"
        };
        const url = "https://accounts.nintendo.com/connect/1.0.0/authorize?" + new URLSearchParams(body);
        const res = await fetch.get({
            url,
            headers: {
                "Host": "accounts.nintendo.com",
                "Connection": "keep-alive",
                "Cache-Control": "max-age=0",
                "Upgrade-Insecure-Requests": "1",
                "User-Agent": DEFAULT_APP_USER_AGENT,
                "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8n",
                "DNT": "1",
                "Accept-Encoding": "gzip,deflate,br"
            }
        });
        return {
            authCodeVerifier,
            url: res.url
        };
    } else {
        const { login , authCodeVerifier  } = step2;
        const loginURL = new URL(login);
        const params = new URLSearchParams(loginURL.hash.substring(1));
        const sessionTokenCode = params.get("session_token_code");
        if (!sessionTokenCode) {
            throw new Error("No session token code provided");
        }
        const sessionToken = await getSessionToken({
            fetch,
            sessionTokenCode,
            authCodeVerifier
        });
        if (!sessionToken) {
            throw new Error("No session token found");
        }
        return {
            sessionToken
        };
    }
}
async function loginManually(env) {
    const { prompts: { promptLogin  }  } = env;
    const step1 = await loginSteps(env);
    const { url , authCodeVerifier  } = step1;
    const login = (await promptLogin(url)).trim();
    if (!login) {
        throw new Error("No login URL provided");
    }
    const step2 = await loginSteps(env, {
        authCodeVerifier,
        login
    });
    return step2.sessionToken;
}
async function getGToken({ fApi , sessionToken , env  }) {
    const fetch = env.newFetcher();
    const idResp = await fetch.post({
        url: "https://accounts.nintendo.com/connect/1.0.0/api/token",
        headers: {
            "Host": "accounts.nintendo.com",
            "Accept-Encoding": "gzip",
            "Content-Type": "application/json",
            "Accept": "application/json",
            "Connection": "Keep-Alive",
            "User-Agent": "Dalvik/2.1.0 (Linux; U; Android 7.1.2)"
        },
        body: JSON.stringify({
            "client_id": "71b963c1b7b6d119",
            "session_token": sessionToken,
            "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer-session-token"
        })
    });
    const idRespJson = await idResp.json();
    const { access_token: accessToken , id_token: idToken  } = idRespJson;
    if (!accessToken || !idToken) {
        throw new APIError({
            response: idResp,
            json: idRespJson,
            message: "No access_token or id_token found"
        });
    }
    const uiResp = await fetch.get({
        url: "https://api.accounts.nintendo.com/2.0.0/users/me",
        headers: {
            "User-Agent": "NASDKAPI; Android",
            "Content-Type": "application/json",
            "Accept": "application/json",
            "Authorization": `Bearer ${accessToken}`,
            "Host": "api.accounts.nintendo.com",
            "Connection": "Keep-Alive",
            "Accept-Encoding": "gzip"
        }
    });
    const uiRespJson = await uiResp.json();
    const { nickname , birthday , language , country , id: userId  } = uiRespJson;
    const getIdToken2 = async (idToken)=>{
        const { f , request_id: requestId , timestamp  } = await callImink({
            fApi,
            step: 1,
            idToken,
            userId,
            env
        });
        const resp = await fetch.post({
            url: "https://api-lp1.znc.srv.nintendo.net/v3/Account/Login",
            headers: {
                "X-Platform": "Android",
                "X-ProductVersion": NSOAPP_VERSION,
                "Content-Type": "application/json; charset=utf-8",
                "Connection": "Keep-Alive",
                "Accept-Encoding": "gzip",
                "User-Agent": `com.nintendo.znca/${NSOAPP_VERSION}(Android/7.1.2)`
            },
            body: JSON.stringify({
                parameter: {
                    "f": f,
                    "language": language,
                    "naBirthday": birthday,
                    "naCountry": country,
                    "naIdToken": idToken,
                    "requestId": requestId,
                    "timestamp": timestamp
                }
            })
        });
        const respJson = await resp.json();
        const idToken2 = respJson?.result?.webApiServerCredential?.accessToken;
        const coralUserId = respJson?.result?.user?.id?.toString();
        if (!idToken2 || !coralUserId) {
            throw new APIError({
                response: resp,
                json: respJson,
                message: `No idToken2 or coralUserId found. Please try again later. (${idToken2.length}, ${coralUserId.length})`
            });
        }
        return [
            idToken2,
            coralUserId
        ];
    };
    const getGToken = async (idToken, coralUserId)=>{
        const { f , request_id: requestId , timestamp  } = await callImink({
            step: 2,
            idToken,
            fApi,
            userId,
            coralUserId,
            env
        });
        const resp = await fetch.post({
            url: "https://api-lp1.znc.srv.nintendo.net/v2/Game/GetWebServiceToken",
            headers: {
                "X-Platform": "Android",
                "X-ProductVersion": NSOAPP_VERSION,
                "Authorization": `Bearer ${idToken}`,
                "Content-Type": "application/json; charset=utf-8",
                "Accept-Encoding": "gzip",
                "User-Agent": `com.nintendo.znca/${NSOAPP_VERSION}(Android/7.1.2)`
            },
            body: JSON.stringify({
                parameter: {
                    "f": f,
                    "id": 4834290508791808,
                    "registrationToken": idToken,
                    "requestId": requestId,
                    "timestamp": timestamp
                }
            })
        });
        const respJson = await resp.json();
        const webServiceToken = respJson?.result?.accessToken;
        if (!webServiceToken) {
            throw new APIError({
                response: resp,
                json: respJson,
                message: "No webServiceToken found"
            });
        }
        return webServiceToken;
    };
    const [idToken2, coralUserId] = await retry(()=>getIdToken2(idToken));
    const webServiceToken = await retry(()=>getGToken(idToken2, coralUserId));
    return {
        webServiceToken,
        nickname,
        userCountry: country,
        userLang: language
    };
}
async function getBulletToken({ webServiceToken , appUserAgent =DEFAULT_APP_USER_AGENT , userLang , userCountry , env  }) {
    const { post  } = env.newFetcher({
        cookies: [
            {
                name: "_gtoken",
                value: webServiceToken,
                domain: "api.lp1.av5ja.srv.nintendo.net"
            }
        ]
    });
    const resp = await post({
        url: "https://api.lp1.av5ja.srv.nintendo.net/api/bullet_tokens",
        headers: {
            "Content-Type": "application/json",
            "Accept-Language": userLang,
            "User-Agent": appUserAgent,
            "X-Web-View-Ver": WEB_VIEW_VERSION,
            "X-NACOUNTRY": userCountry,
            "Accept": "*/*",
            "Origin": "https://api.lp1.av5ja.srv.nintendo.net",
            "X-Requested-With": "com.nintendo.znca"
        }
    });
    if (resp.status == 401) {
        throw new APIError({
            response: resp,
            message: "Unauthorized error (ERROR_INVALID_GAME_WEB_TOKEN). Cannot fetch tokens at this time."
        });
    }
    if (resp.status == 403) {
        throw new APIError({
            response: resp,
            message: "Forbidden error (ERROR_OBSOLETE_VERSION). Cannot fetch tokens at this time."
        });
    }
    if (resp.status == 204) {
        throw new APIError({
            response: resp,
            message: "Cannot access SplatNet 3 without having played online."
        });
    }
    if (resp.status !== 201) {
        throw new APIError({
            response: resp,
            message: "Not 201"
        });
    }
    const respJson = await resp.json();
    const { bulletToken  } = respJson;
    if (typeof bulletToken !== "string") {
        throw new APIError({
            response: resp,
            json: respJson,
            message: "No bulletToken found"
        });
    }
    return bulletToken;
}
function random(size) {
    return crypto.getRandomValues(new Uint8Array(size)).buffer;
}
async function getSessionToken({ fetch , sessionTokenCode , authCodeVerifier  }) {
    const resp = await fetch.post({
        url: "https://accounts.nintendo.com/connect/1.0.0/api/session_token",
        headers: {
            "User-Agent": `OnlineLounge/${NSOAPP_VERSION} NASDKAPI Android`,
            "Accept-Language": "en-US",
            "Accept": "application/json",
            "Content-Type": "application/x-www-form-urlencoded",
            "Host": "accounts.nintendo.com",
            "Connection": "Keep-Alive",
            "Accept-Encoding": "gzip"
        },
        body: new URLSearchParams({
            "client_id": "71b963c1b7b6d119",
            "session_token_code": sessionTokenCode,
            "session_token_code_verifier": authCodeVerifier
        })
    });
    const json = await resp.json();
    if (json.error) {
        throw new APIError({
            response: resp,
            json,
            message: "Error getting session token"
        });
    }
    return json["session_token"];
}
async function callImink(params) {
    const { fApi , step , idToken , userId , coralUserId , env  } = params;
    const { post  } = env.newFetcher();
    const resp = await post({
        url: fApi,
        headers: {
            "User-Agent": USERAGENT,
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            "token": idToken,
            "hash_method": step,
            "na_id": userId,
            "coral_user_id": coralUserId
        })
    });
    return await resp.json();
}
const DEFAULT_ENV = {
    prompts: {
        promptLogin: async (url)=>{
            console.log("Navigate to this URL in your browser:");
            console.log(url);
            console.log('Log in, right click the "Select this account" button, copy the link address, and paste it below:');
            return await readline();
        },
        prompt: async (tips)=>{
            console.log(tips);
            return await readline();
        }
    },
    logger: {
        debug: console.debug,
        log: console.log,
        warn: console.warn,
        error: console.error
    },
    newFetcher: ({ cookies  } = {})=>{
        const cookieJar = new CookieJar(cookies);
        const fetch = wrapFetch({
            cookieJar
        });
        return {
            async get ({ url , headers  }) {
                return await fetch(url, {
                    method: "GET",
                    headers
                });
            },
            async post ({ url , body , headers  }) {
                return await fetch(url, {
                    method: "POST",
                    headers,
                    body
                });
            }
        };
    }
};
const DEFAULT_STATE = {
    cacheDir: "./cache",
    fGen: "https://api.imink.app/f",
    fileExportPath: "./export",
    monitorInterval: 500
};
class FileStateBackend {
    path;
    constructor(path){
        this.path = path;
    }
    async read() {
        const data = await Deno.readTextFile(this.path);
        const json = JSON.parse(data);
        return json;
    }
    async write(newState) {
        const data = JSON.stringify(newState, undefined, 2);
        const swapPath = `${this.path}.swap`;
        await Deno.writeTextFile(swapPath, data);
        await Deno.rename(swapPath, this.path);
    }
}
class Profile {
    _state;
    stateBackend;
    env;
    constructor({ stateBackend , env =DEFAULT_ENV  }){
        this.stateBackend = stateBackend;
        this.env = env;
    }
    get state() {
        if (!this._state) {
            throw new Error("state is not initialized");
        }
        return this._state;
    }
    async writeState(newState) {
        this._state = newState;
        await this.stateBackend.write(newState);
    }
    async readState() {
        try {
            const json = await this.stateBackend.read();
            this._state = {
                ...DEFAULT_STATE,
                ...json
            };
        } catch (e) {
            this.env.logger.warn(`Failed to read config file, create new config file. (${e})`);
            await this.writeState(DEFAULT_STATE);
        }
    }
}
var BattleListType;
(function(BattleListType) {
    BattleListType[BattleListType["Latest"] = 0] = "Latest";
    BattleListType[BattleListType["Regular"] = 1] = "Regular";
    BattleListType[BattleListType["Bankara"] = 2] = "Bankara";
    BattleListType[BattleListType["Event"] = 3] = "Event";
    BattleListType[BattleListType["XBattle"] = 4] = "XBattle";
    BattleListType[BattleListType["Private"] = 5] = "Private";
    BattleListType[BattleListType["Coop"] = 6] = "Coop";
})(BattleListType || (BattleListType = {}));
var SummaryEnum;
(function(SummaryEnum) {
    SummaryEnum[SummaryEnum["ConfigureAnalyticsQuery"] = Queries.ConfigureAnalyticsQuery] = "ConfigureAnalyticsQuery";
    SummaryEnum[SummaryEnum["HistoryRecordQuery"] = Queries.HistoryRecordQuery] = "HistoryRecordQuery";
    SummaryEnum[SummaryEnum["CoopHistoryQuery"] = Queries.CoopHistoryQuery] = "CoopHistoryQuery";
})(SummaryEnum || (SummaryEnum = {}));
class Splatnet3 {
    profile;
    env;
    constructor({ profile , env =DEFAULT_ENV  }){
        this.profile = profile;
        this.env = env;
    }
    async request(query, ...rest) {
        const doRequest = async ()=>{
            const state = this.profile.state;
            const variables = rest?.[0] ?? {};
            const body = {
                extensions: {
                    persistedQuery: {
                        sha256Hash: query,
                        version: 1
                    }
                },
                variables
            };
            const { post  } = this.env.newFetcher();
            const resp = await post({
                url: SPLATNET3_ENDPOINT,
                headers: {
                    "Authorization": `Bearer ${state.loginState?.bulletToken}`,
                    "Accept-Language": state.userLang ?? "en-US",
                    "User-Agent": state.appUserAgent ?? DEFAULT_APP_USER_AGENT,
                    "X-Web-View-Ver": WEB_VIEW_VERSION,
                    "Content-Type": "application/json",
                    "Accept": "*/*",
                    "Origin": "https://api.lp1.av5ja.srv.nintendo.net",
                    "X-Requested-With": "com.nintendo.znca",
                    "Referer": `https://api.lp1.av5ja.srv.nintendo.net/?lang=${state.userLang}&na_country=${state.userCountry}&na_lang=${state.userLang}`,
                    "Accept-Encoding": "gzip, deflate",
                    "Cookie": `_gtoken: ${state.loginState?.gToken}`
                },
                body: JSON.stringify(body)
            });
            if (resp.status !== 200) {
                throw new APIError({
                    response: resp,
                    message: "Splatnet3 request failed"
                });
            }
            const json = await resp.json();
            if ("errors" in json) {
                throw new APIError({
                    response: resp,
                    json,
                    message: `Splatnet3 request failed(${json.errors?.[0].message})`
                });
            }
            return json.data;
        };
        try {
            return await doRequest();
        } catch (e) {
            if (isTokenExpired(e)) {
                await this.fetchToken();
                return await doRequest();
            }
            throw e;
        }
    }
    async fetchToken() {
        const state = this.profile.state;
        const sessionToken = state.loginState?.sessionToken;
        if (!sessionToken) {
            throw new Error("Session token is not set.");
        }
        const { webServiceToken , userCountry , userLang  } = await getGToken({
            fApi: state.fGen,
            sessionToken,
            env: this.env
        });
        const bulletToken = await getBulletToken({
            webServiceToken,
            userLang,
            userCountry,
            appUserAgent: state.appUserAgent,
            env: this.env
        });
        await this.profile.writeState({
            ...state,
            loginState: {
                ...state.loginState,
                gToken: webServiceToken,
                bulletToken
            },
            userLang: state.userLang ?? userLang,
            userCountry: state.userCountry ?? userCountry
        });
    }
    BATTLE_LIST_TYPE_MAP = {
        [BattleListType.Latest]: ()=>this.request(Queries.LatestBattleHistoriesQuery).then((r)=>getIdsFromGroups(r.latestBattleHistories)),
        [BattleListType.Regular]: ()=>this.request(Queries.RegularBattleHistoriesQuery).then((r)=>getIdsFromGroups(r.regularBattleHistories)),
        [BattleListType.Bankara]: ()=>this.request(Queries.BankaraBattleHistoriesQuery).then((r)=>getIdsFromGroups(r.bankaraBattleHistories)),
        [BattleListType.XBattle]: ()=>this.request(Queries.XBattleHistoriesQuery).then((r)=>getIdsFromGroups(r.xBattleHistories)),
        [BattleListType.Event]: ()=>this.request(Queries.EventBattleHistoriesQuery).then((r)=>getIdsFromGroups(r.eventBattleHistories)),
        [BattleListType.Private]: ()=>this.request(Queries.PrivateBattleHistoriesQuery).then((r)=>getIdsFromGroups(r.privateBattleHistories)),
        [BattleListType.Coop]: ()=>this.request(Queries.CoopHistoryQuery).then((r)=>getIdsFromGroups(r.coopResult))
    };
    async checkToken() {
        const state = this.profile.state;
        if (!state.loginState?.sessionToken || !state.loginState?.bulletToken || !state.loginState?.gToken) {
            return false;
        }
        try {
            await this.request(Queries.ConfigureAnalyticsQuery);
            return true;
        } catch (_e) {
            return false;
        }
    }
    async getBattleList(battleListType = BattleListType.Latest) {
        return await this.BATTLE_LIST_TYPE_MAP[battleListType]();
    }
    async getAllBattleList() {
        const ALL_TYPE = [
            BattleListType.Regular,
            BattleListType.Bankara,
            BattleListType.XBattle,
            BattleListType.Event,
            BattleListType.Private
        ];
        const ids = [];
        for (const type of ALL_TYPE){
            ids.push(...await this.getBattleList(type));
        }
        const timeMap = new Map(ids.map((id)=>[
                id,
                battleTime(id)
            ]));
        return ids.sort((a, b)=>timeMap.get(b).getTime() - timeMap.get(a).getTime());
    }
    getBattleDetail(id) {
        return this.request(Queries.VsHistoryDetailQuery, {
            vsResultId: id
        });
    }
    getCoopDetail(id) {
        return this.request(Queries.CoopHistoryDetailQuery, {
            coopHistoryDetailId: id
        });
    }
    async getBankaraBattleHistories() {
        const resp = await this.request(Queries.BankaraBattleHistoriesQuery);
        return resp;
    }
    async getXBattleHistories() {
        return await this.request(Queries.XBattleHistoriesQuery);
    }
    async getCoopHistories() {
        const resp = await this.request(Queries.CoopHistoryQuery);
        return resp;
    }
    async getGearPower() {
        const resp = await this.request(Queries.myOutfitCommonDataFilteringConditionQuery);
        return resp;
    }
    async getLatestBattleHistoriesQuery() {
        const resp = await this.request(Queries.LatestBattleHistoriesQuery);
        return resp;
    }
    async getGears() {
        const resp = await this.request(Queries.myOutfitCommonDataEquipmentsQuery);
        return resp;
    }
    async getSummary() {
        const ConfigureAnalyticsQuery = await this.request(Queries.ConfigureAnalyticsQuery);
        const HistoryRecordQuery = await this.request(Queries.HistoryRecordQuery);
        const CoopHistoryQuery = await this.request(Queries.CoopHistoryQuery);
        const getFirstBattleId = async ()=>{
            const latest = await this.request(Queries.LatestBattleHistoriesQuery);
            const id = latest?.latestBattleHistories?.historyGroups?.nodes?.[0]?.historyDetails?.nodes?.[0]?.id;
            return id;
        };
        const id = CoopHistoryQuery?.coopResult?.historyGroups?.nodes?.[0]?.historyDetails?.nodes?.[0]?.id ?? await getFirstBattleId();
        if (!id) {
            throw new Error("No battle id found");
        }
        const { uid  } = parseHistoryDetailId(id);
        return {
            uid,
            ConfigureAnalyticsQuery,
            HistoryRecordQuery,
            CoopHistoryQuery
        };
    }
}
function getIdsFromGroups({ historyGroups  }) {
    return historyGroups.nodes.flatMap((i)=>i.historyDetails.nodes).map((i)=>i.id);
}
function isTokenExpired(e) {
    if (e instanceof APIError) {
        return e.response.status === 401;
    } else {
        return false;
    }
}
class MemoryCache {
    cache = {};
    async read(key) {
        return this.cache[key];
    }
    async write(key, value) {
        this.cache[key] = value;
    }
}
class FileCache {
    path;
    constructor(path){
        this.path = path;
    }
    async getPath(key) {
        await Deno.mkdir(this.path, {
            recursive: true
        });
        const hash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(key));
        const hashHex = Array.from(new Uint8Array(hash)).map((b)=>b.toString(16).padStart(2, "0")).join("");
        return mod10.join(this.path, hashHex);
    }
    async read(key) {
        const path = await this.getPath(key);
        try {
            const data = await Deno.readTextFile(path);
            return JSON.parse(data);
        } catch (e) {
            if (e instanceof Deno.errors.NotFound) {
                return undefined;
            }
            throw e;
        }
    }
    async write(key, value) {
        const path = await this.getPath(key);
        const encoder = new TextEncoder();
        const data = encoder.encode(JSON.stringify(value));
        const swapPath = `${path}.swap`;
        await Deno.writeFile(swapPath, data);
        await Deno.rename(swapPath, path);
    }
}
const __default = JSON.parse("[\n  {\n    \"key\": \"ink_saver_main\",\n    \"name\": {\n      \"de-DE\": \"Hauptverbrauch\",\n      \"en-GB\": \"Ink Saver (Main)\",\n      \"en-US\": \"Ink Saver (Main)\",\n      \"es-ES\": \"Tintahorro (ppal.)\",\n      \"es-MX\": \"Ahorro tinta (ppal.)\",\n      \"fr-CA\": \"Encrémenteur (pr.)\",\n      \"fr-FR\": \"Encrémenteur (pr.)\",\n      \"it-IT\": \"Eco-colore princ.\",\n      \"ja-JP\": \"インク効率アップ(メイン)\",\n      \"ko-KR\": \"잉크 효율 업(메인)\",\n      \"nl-NL\": \"Hoofdspaarder\",\n      \"ru-RU\": \"Основной баллон X\",\n      \"zh-CN\": \"提升墨汁效率（主要武器）\",\n      \"zh-TW\": \"提升墨汁效率（主要武器）\"\n    },\n    \"primary_only\": false\n  },\n  {\n    \"key\": \"ink_saver_sub\",\n    \"name\": {\n      \"de-DE\": \"Sekundärverbrauch\",\n      \"en-GB\": \"Ink Saver (Sub)\",\n      \"en-US\": \"Ink Saver (Sub)\",\n      \"es-ES\": \"Tintahorro (sec.)\",\n      \"es-MX\": \"Ahorro tinta (sec.)\",\n      \"fr-CA\": \"Encrémenteur (sec.)\",\n      \"fr-FR\": \"Encrémenteur (sec.)\",\n      \"it-IT\": \"Eco-colore second.\",\n      \"ja-JP\": \"インク効率アップ(サブ)\",\n      \"ko-KR\": \"잉크 효율 업(서브)\",\n      \"nl-NL\": \"Subspaarder\",\n      \"ru-RU\": \"Запасной баллон X\",\n      \"zh-CN\": \"提升墨汁效率（次要武器）\",\n      \"zh-TW\": \"提升墨汁效率（次要武器）\"\n    },\n    \"primary_only\": false\n  },\n  {\n    \"key\": \"ink_recovery_up\",\n    \"name\": {\n      \"de-DE\": \"Regeneration +\",\n      \"en-GB\": \"Ink Recovery Up\",\n      \"en-US\": \"Ink Recovery Up\",\n      \"es-ES\": \"Recarga rápida\",\n      \"es-MX\": \"Mejor recarga tinta\",\n      \"fr-CA\": \"Levée d'encre\",\n      \"fr-FR\": \"Levée d'encre\",\n      \"it-IT\": \"Recupero colore +\",\n      \"ja-JP\": \"インク回復力アップ\",\n      \"ko-KR\": \"잉크 회복력 업\",\n      \"nl-NL\": \"Inktvulling\",\n      \"ru-RU\": \"Быстрый баллон\",\n      \"zh-CN\": \"提升墨汁回复力\",\n      \"zh-TW\": \"提升墨汁回復力\"\n    },\n    \"primary_only\": false\n  },\n  {\n    \"key\": \"run_speed_up\",\n    \"name\": {\n      \"de-DE\": \"Lauftempo +\",\n      \"en-GB\": \"Run Speed Up\",\n      \"en-US\": \"Run Speed Up\",\n      \"es-ES\": \"Supercarrera\",\n      \"es-MX\": \"Carrera acelerada\",\n      \"fr-CA\": \"Course à pied\",\n      \"fr-FR\": \"Course à pied\",\n      \"it-IT\": \"Velocità +\",\n      \"ja-JP\": \"ヒト移動速度アップ\",\n      \"ko-KR\": \"인간 이동 속도 업\",\n      \"nl-NL\": \"Hardloper\",\n      \"ru-RU\": \"Спринтер\",\n      \"zh-CN\": \"提升人类移动速度\",\n      \"zh-TW\": \"提升人類移動速度\"\n    },\n    \"primary_only\": false\n  },\n  {\n    \"key\": \"swim_speed_up\",\n    \"name\": {\n      \"de-DE\": \"Schwimmtempo +\",\n      \"en-GB\": \"Swim Speed Up\",\n      \"en-US\": \"Swim Speed Up\",\n      \"es-ES\": \"Superbuceo\",\n      \"es-MX\": \"Nado acelerado\",\n      \"fr-CA\": \"Turbo-calmar\",\n      \"fr-FR\": \"Turbo-calamar\",\n      \"it-IT\": \"Velocità nuoto +\",\n      \"ja-JP\": \"イカダッシュ速度アップ\",\n      \"ko-KR\": \"징어대시 속도 업\",\n      \"nl-NL\": \"Zwemdiploma\",\n      \"ru-RU\": \"Плавунец\",\n      \"zh-CN\": \"提升鱿鱼冲刺速度\",\n      \"zh-TW\": \"提升魷魚衝刺速度\"\n    },\n    \"primary_only\": false\n  },\n  {\n    \"key\": \"special_charge_up\",\n    \"name\": {\n      \"de-DE\": \"Spezialladezeit +\",\n      \"en-GB\": \"Special Charge Up\",\n      \"en-US\": \"Special Charge Up\",\n      \"es-ES\": \"Recarga especial\",\n      \"es-MX\": \"Recarga especial\",\n      \"fr-CA\": \"Jauge spéciale +\",\n      \"fr-FR\": \"Jauge spéciale +\",\n      \"it-IT\": \"Ricarica speciale +\",\n      \"ja-JP\": \"スペシャル増加量アップ\",\n      \"ko-KR\": \"스페셜 증가량 업\",\n      \"nl-NL\": \"Speciaallader\",\n      \"ru-RU\": \"Особый насос\",\n      \"zh-CN\": \"提升特殊武器增加量\",\n      \"zh-TW\": \"提升特殊武器增加量\"\n    },\n    \"primary_only\": false\n  },\n  {\n    \"key\": \"special_saver\",\n    \"name\": {\n      \"de-DE\": \"Spezialabzug -\",\n      \"en-GB\": \"Special Saver\",\n      \"en-US\": \"Special Saver\",\n      \"es-ES\": \"Reducción especial\",\n      \"es-MX\": \"Ahorro especial\",\n      \"fr-CA\": \"Baisse spéciale -\",\n      \"fr-FR\": \"Baisse spéciale -\",\n      \"it-IT\": \"Riduzione speciale -\",\n      \"ja-JP\": \"スペシャル減少量ダウン\",\n      \"ko-KR\": \"스페셜 감소량 다운\",\n      \"nl-NL\": \"Speciaalspaarder\",\n      \"ru-RU\": \"Особый резерв\",\n      \"zh-CN\": \"降低特殊武器减少量\",\n      \"zh-TW\": \"降低特殊武器減少量\"\n    },\n    \"primary_only\": false\n  },\n  {\n    \"key\": \"special_power_up\",\n    \"name\": {\n      \"de-DE\": \"Spezialstärke +\",\n      \"en-GB\": \"Special Power Up\",\n      \"en-US\": \"Special Power Up\",\n      \"es-ES\": \"Superarma especial\",\n      \"es-MX\": \"Mejora especial\",\n      \"fr-CA\": \"Arme spéciale +\",\n      \"fr-FR\": \"Arme spéciale +\",\n      \"it-IT\": \"Arma speciale +\",\n      \"ja-JP\": \"スペシャル性能アップ\",\n      \"ko-KR\": \"스페셜 성능 업\",\n      \"nl-NL\": \"Specialist\",\n      \"ru-RU\": \"Особый подход\",\n      \"zh-CN\": \"提升特殊武器性能\",\n      \"zh-TW\": \"提升特殊武器性能\"\n    },\n    \"primary_only\": false\n  },\n  {\n    \"key\": \"quick_respawn\",\n    \"name\": {\n      \"de-DE\": \"Schnelle Rückkehr\",\n      \"en-GB\": \"Quick Respawn\",\n      \"en-US\": \"Quick Respawn\",\n      \"es-ES\": \"Retorno exprés\",\n      \"es-MX\": \"Regeneración rápida\",\n      \"fr-CA\": \"Sans temps mort\",\n      \"fr-FR\": \"Sans temps morts\",\n      \"it-IT\": \"Il tempo è colore\",\n      \"ja-JP\": \"復活時間短縮\",\n      \"ko-KR\": \"부활 시간 단축\",\n      \"nl-NL\": \"Comeback\",\n      \"ru-RU\": \"Феникс\",\n      \"zh-CN\": \"缩短复活时间\",\n      \"zh-TW\": \"縮短復活時間\"\n    },\n    \"primary_only\": false\n  },\n  {\n    \"key\": \"quick_super_jump\",\n    \"name\": {\n      \"de-DE\": \"Supersprung +\",\n      \"en-GB\": \"Quick Super Jump\",\n      \"en-US\": \"Quick Super Jump\",\n      \"es-ES\": \"Supersalto rápido\",\n      \"es-MX\": \"Supersalto rápido\",\n      \"fr-CA\": \"Aérodynamisme\",\n      \"fr-FR\": \"Aérodynamisme\",\n      \"it-IT\": \"Salti super e veloci\",\n      \"ja-JP\": \"スーパージャンプ時間短縮\",\n      \"ko-KR\": \"슈퍼 점프 시간 단축\",\n      \"nl-NL\": \"Turbosprong\",\n      \"ru-RU\": \"Суперпрыгун\",\n      \"zh-CN\": \"缩短超级跳跃时间\",\n      \"zh-TW\": \"縮短超級跳躍時間\"\n    },\n    \"primary_only\": false\n  },\n  {\n    \"key\": \"sub_power_up\",\n    \"name\": {\n      \"de-DE\": \"Sekundärstärke +\",\n      \"en-GB\": \"Sub Power Up\",\n      \"en-US\": \"Sub Power Up\",\n      \"es-ES\": \"Superarma secundaria\",\n      \"es-MX\": \"Mejora secundaria\",\n      \"fr-CA\": \"Arme secondaire +\",\n      \"fr-FR\": \"Arme secondaire +\",\n      \"it-IT\": \"Arma secondaria +\",\n      \"ja-JP\": \"サブ性能アップ\",\n      \"ko-KR\": \"서브 성능 업\",\n      \"nl-NL\": \"Subtopper\",\n      \"ru-RU\": \"Про-Запас\",\n      \"zh-CN\": \"提升次要武器性能\",\n      \"zh-TW\": \"提升次要武器性能\"\n    },\n    \"primary_only\": false\n  },\n  {\n    \"key\": \"ink_resistance_up\",\n    \"name\": {\n      \"de-DE\": \"Tintentoleranz +\",\n      \"en-GB\": \"Ink Resistance Up\",\n      \"en-US\": \"Ink Resistance Up\",\n      \"es-ES\": \"Impermeabilidad\",\n      \"es-MX\": \"Impermeabilidad\",\n      \"fr-CA\": \"Imperméabilité\",\n      \"fr-FR\": \"Pieds au sec\",\n      \"it-IT\": \"Scarpe impermeabili\",\n      \"ja-JP\": \"相手インク影響軽減\",\n      \"ko-KR\": \"상대 잉크 영향 감소\",\n      \"nl-NL\": \"Inkttolerantie\",\n      \"ru-RU\": \"Краскостойкость\",\n      \"zh-CN\": \"减轻对手墨汁影响\",\n      \"zh-TW\": \"減輕對手墨汁影響\"\n    },\n    \"primary_only\": false\n  },\n  {\n    \"key\": \"sub_resistance_up\",\n    \"name\": {\n      \"de-DE\": \"Sekundärschutz +\",\n      \"en-GB\": \"Sub Resistance Up\",\n      \"en-US\": \"Sub Resistance Up\",\n      \"es-ES\": \"Resistencia secundaria\",\n      \"es-MX\": \"Resistencia secundaria\",\n      \"fr-CA\": \"Filtre à secondaires\",\n      \"fr-FR\": \"Filtre à secondaires\",\n      \"it-IT\": \"Arma sec. impermeabile\",\n      \"ja-JP\": \"サブ影響軽減\",\n      \"ko-KR\": \"서브 영향 감소\",\n      \"nl-NL\": \"Subdemper\",\n      \"ru-RU\": \"Стойкость запаса\",\n      \"zh-CN\": \"减轻次要武器影响\",\n      \"zh-TW\": \"減輕次要武器影響\"\n    },\n    \"primary_only\": false\n  },\n  {\n    \"key\": \"intensify_action\",\n    \"name\": {\n      \"de-DE\": \"Action +\",\n      \"en-GB\": \"Intensify Action\",\n      \"en-US\": \"Intensify Action\",\n      \"es-ES\": \"Agilidad extra\",\n      \"es-MX\": \"Agilidad extra\",\n      \"fr-CA\": \"Feu de l'action\",\n      \"fr-FR\": \"Feu de l'action\",\n      \"it-IT\": \"Intensificazione\",\n      \"ja-JP\": \"アクション強化\",\n      \"ko-KR\": \"액션 강화\",\n      \"nl-NL\": \"Actie-assistentie\",\n      \"ru-RU\": \"Ультраудар\",\n      \"zh-CN\": \"行动强化\",\n      \"zh-TW\": \"行動強化\"\n    },\n    \"primary_only\": false\n  },\n  {\n    \"key\": \"opening_gambit\",\n    \"name\": {\n      \"de-DE\": \"Startvorteil\",\n      \"en-GB\": \"Opening Gambit\",\n      \"en-US\": \"Opening Gambit\",\n      \"es-ES\": \"Acelerón de salida\",\n      \"es-MX\": \"Acelerón de salida\",\n      \"fr-CA\": \"Départ toute allure\",\n      \"fr-FR\": \"Chapeaux de roue\",\n      \"it-IT\": \"Partenza a razzo\",\n      \"ja-JP\": \"スタートダッシュ\",\n      \"ko-KR\": \"스타트 대시\",\n      \"nl-NL\": \"Vliegende start\",\n      \"ru-RU\": \"Стартовый спурт\",\n      \"zh-CN\": \"最初冲刺\",\n      \"zh-TW\": \"最初衝刺\"\n    },\n    \"primary_only\": true\n  },\n  {\n    \"key\": \"last_ditch_effort\",\n    \"name\": {\n      \"de-DE\": \"Endspurt\",\n      \"en-GB\": \"Last-Ditch Effort\",\n      \"en-US\": \"Last-Ditch Effort\",\n      \"es-ES\": \"Sprint final\",\n      \"es-MX\": \"Último recurso\",\n      \"fr-CA\": \"Ultime sursaut\",\n      \"fr-FR\": \"Ultime sursaut\",\n      \"it-IT\": \"Splash finale\",\n      \"ja-JP\": \"ラストスパート\",\n      \"ko-KR\": \"라스트 스퍼트\",\n      \"nl-NL\": \"Eindsprint\",\n      \"ru-RU\": \"Финишный спурт\",\n      \"zh-CN\": \"最后冲刺\",\n      \"zh-TW\": \"最後衝刺\"\n    },\n    \"primary_only\": true\n  },\n  {\n    \"key\": \"tenacity\",\n    \"name\": {\n      \"de-DE\": \"Zähigkeit\",\n      \"en-GB\": \"Tenacity\",\n      \"en-US\": \"Tenacity\",\n      \"es-ES\": \"Ventaja\",\n      \"es-MX\": \"Tenacidad\",\n      \"fr-CA\": \"Ténacité\",\n      \"fr-FR\": \"Justice\",\n      \"it-IT\": \"Tenacia\",\n      \"ja-JP\": \"逆境強化\",\n      \"ko-KR\": \"역경 강화\",\n      \"nl-NL\": \"Volharding\",\n      \"ru-RU\": \"Компенсатор\",\n      \"zh-CN\": \"逆境强化\",\n      \"zh-TW\": \"逆境強化\"\n    },\n    \"primary_only\": true\n  },\n  {\n    \"key\": \"comeback\",\n    \"name\": {\n      \"de-DE\": \"Rückkehr\",\n      \"en-GB\": \"Comeback\",\n      \"en-US\": \"Comeback\",\n      \"es-ES\": \"Remontada\",\n      \"es-MX\": \"Remonte\",\n      \"fr-CA\": \"Retour\",\n      \"fr-FR\": \"Come-back\",\n      \"it-IT\": \"Gran ritorno\",\n      \"ja-JP\": \"カムバック\",\n      \"ko-KR\": \"컴백\",\n      \"nl-NL\": \"Opfrisser\",\n      \"ru-RU\": \"Ответный удар\",\n      \"zh-CN\": \"回归\",\n      \"zh-TW\": \"回歸\"\n    },\n    \"primary_only\": true\n  },\n  {\n    \"key\": \"ninja_squid\",\n    \"name\": {\n      \"de-DE\": \"Tintenfisch-Ninja\",\n      \"en-GB\": \"Ninja Squid\",\n      \"en-US\": \"Ninja Squid\",\n      \"es-ES\": \"Ninjalamar\",\n      \"es-MX\": \"Ninjalamar\",\n      \"fr-CA\": \"Ninjalmar\",\n      \"fr-FR\": \"Ninjalamar\",\n      \"it-IT\": \"Calamaro ninja\",\n      \"ja-JP\": \"イカニンジャ\",\n      \"ko-KR\": \"징어닌자\",\n      \"nl-NL\": \"Ninja-inktvis\",\n      \"ru-RU\": \"Мимикрия\",\n      \"zh-CN\": \"鱿鱼忍者\",\n      \"zh-TW\": \"魷魚忍者\"\n    },\n    \"primary_only\": true\n  },\n  {\n    \"key\": \"haunt\",\n    \"name\": {\n      \"de-DE\": \"Vergeltung\",\n      \"en-GB\": \"Haunt\",\n      \"en-US\": \"Haunt\",\n      \"es-ES\": \"Represalia\",\n      \"es-MX\": \"Resentimiento\",\n      \"fr-CA\": \"Vengeance\",\n      \"fr-FR\": \"Revanche\",\n      \"it-IT\": \"Rappresaglia\",\n      \"ja-JP\": \"リベンジ\",\n      \"ko-KR\": \"리벤지\",\n      \"nl-NL\": \"Revanche\",\n      \"ru-RU\": \"Вендетта\",\n      \"zh-CN\": \"复仇\",\n      \"zh-TW\": \"復仇\"\n    },\n    \"primary_only\": true\n  },\n  {\n    \"key\": \"thermal_ink\",\n    \"name\": {\n      \"de-DE\": \"Markierfarbe\",\n      \"en-GB\": \"Thermal Ink\",\n      \"en-US\": \"Thermal Ink\",\n      \"es-ES\": \"Señuelo\",\n      \"es-MX\": \"Tinta rastreadora\",\n      \"fr-CA\": \"Encre thermique\",\n      \"fr-FR\": \"Encre thermique\",\n      \"it-IT\": \"Inchiostro termico\",\n      \"ja-JP\": \"サーマルインク\",\n      \"ko-KR\": \"서멀 잉크\",\n      \"nl-NL\": \"Markeerstift\",\n      \"ru-RU\": \"Клеймо\",\n      \"zh-CN\": \"热力墨汁\",\n      \"zh-TW\": \"熱力墨汁\"\n    },\n    \"primary_only\": true\n  },\n  {\n    \"key\": \"respawn_punisher\",\n    \"name\": {\n      \"de-DE\": \"Heimsuchung\",\n      \"en-GB\": \"Respawn Punisher\",\n      \"en-US\": \"Respawn Punisher\",\n      \"es-ES\": \"Castigo póstumo\",\n      \"es-MX\": \"Castigo póstumo\",\n      \"fr-CA\": \"Retour perdant\",\n      \"fr-FR\": \"Retour perdant\",\n      \"it-IT\": \"Castigo\",\n      \"ja-JP\": \"復活ペナルティアップ\",\n      \"ko-KR\": \"부활 페널티 업\",\n      \"nl-NL\": \"Repercussie\",\n      \"ru-RU\": \"Кара\",\n      \"zh-CN\": \"提升复活惩罚\",\n      \"zh-TW\": \"提升復活懲罰\"\n    },\n    \"primary_only\": true\n  },\n  {\n    \"key\": \"ability_doubler\",\n    \"name\": {\n      \"de-DE\": \"Effektdoppelung\",\n      \"en-GB\": \"Ability Doubler\",\n      \"en-US\": \"Ability Doubler\",\n      \"es-ES\": \"Duplicador\",\n      \"es-MX\": \"Duplicador\",\n      \"fr-CA\": \"Bonus ×2\",\n      \"fr-FR\": \"Bonus ×2\",\n      \"it-IT\": \"Raddoppiatore\",\n      \"ja-JP\": \"追加ギアパワー倍化\",\n      \"ko-KR\": \"추가 기어 파워 2배\",\n      \"nl-NL\": \"Verdubbelaar\",\n      \"ru-RU\": \"Дупликатор\",\n      \"zh-CN\": \"追加装备能力增倍\",\n      \"zh-TW\": \"追加裝備能力增倍\"\n    },\n    \"primary_only\": true\n  },\n  {\n    \"key\": \"stealth_jump\",\n    \"name\": {\n      \"de-DE\": \"Sprunginfiltration\",\n      \"en-GB\": \"Stealth Jump\",\n      \"en-US\": \"Stealth Jump\",\n      \"es-ES\": \"Supersalto invisible\",\n      \"es-MX\": \"Supersalto invisible\",\n      \"fr-CA\": \"Super saut invisible\",\n      \"fr-FR\": \"Réception réussie\",\n      \"it-IT\": \"Salto al buio\",\n      \"ja-JP\": \"ステルスジャンプ\",\n      \"ko-KR\": \"스텔스 점프\",\n      \"nl-NL\": \"Sluipsprong\",\n      \"ru-RU\": \"Десант\",\n      \"zh-CN\": \"隐身跳跃\",\n      \"zh-TW\": \"隱身跳躍\"\n    },\n    \"primary_only\": true\n  },\n  {\n    \"key\": \"object_shredder\",\n    \"name\": {\n      \"de-DE\": \"Zerstörer\",\n      \"en-GB\": \"Object Shredder\",\n      \"en-US\": \"Object Shredder\",\n      \"es-ES\": \"Demolición\",\n      \"es-MX\": \"Demolición\",\n      \"fr-CA\": \"Démolition\",\n      \"fr-FR\": \"Démolition\",\n      \"it-IT\": \"Demolitore\",\n      \"ja-JP\": \"対物攻撃力アップ\",\n      \"ko-KR\": \"오브젝트 공격력 업\",\n      \"nl-NL\": \"Sloper\",\n      \"ru-RU\": \"Демонтажник\",\n      \"zh-CN\": \"提升对物体攻击力\",\n      \"zh-TW\": \"提升對物體攻擊力\"\n    },\n    \"primary_only\": true\n  },\n  {\n    \"key\": \"drop_roller\",\n    \"name\": {\n      \"de-DE\": \"Tricklandung\",\n      \"en-GB\": \"Drop Roller\",\n      \"en-US\": \"Drop Roller\",\n      \"es-ES\": \"Amortiguador\",\n      \"es-MX\": \"Aterrizaje rodante\",\n      \"fr-CA\": \"Super roulade\",\n      \"fr-FR\": \"Super roulade\",\n      \"it-IT\": \"Atterraggio stiloso\",\n      \"ja-JP\": \"受け身術\",\n      \"ko-KR\": \"낙법\",\n      \"nl-NL\": \"Rolmodel\",\n      \"ru-RU\": \"Акробат\",\n      \"zh-CN\": \"受身术\",\n      \"zh-TW\": \"受身術\"\n    },\n    \"primary_only\": true\n  }\n]");
const COOP_POINT_MAP = {
    0: -20,
    1: -10,
    2: 0,
    3: 20
};
async function checkResponse(resp) {
    if (Math.floor(resp.status / 100) !== 2) {
        const json = await resp.json().catch(()=>undefined);
        throw new APIError({
            response: resp,
            json,
            message: "Failed to fetch data from stat.ink"
        });
    }
}
class StatInkAPI {
    statInkApiKey;
    env;
    statInk;
    FETCH_LOCK;
    cache;
    constructor(statInkApiKey, env){
        this.statInkApiKey = statInkApiKey;
        this.env = env;
        this.statInk = "https://stat.ink";
        this.FETCH_LOCK = new Mutex();
        this.cache = {};
        this._salmonWeaponMap = new Map();
        this.getSalmonWeapon = ()=>this._getCached(`${this.statInk}/api/v3/salmon/weapon?full=1`);
        this.getWeapon = ()=>this._getCached(`${this.statInk}/api/v3/weapon?full=1`);
        this.getAbility = ()=>__default;
        this.getStage = ()=>this._getCached(`${this.statInk}/api/v3/stage`);
        if (statInkApiKey.length !== 43) {
            throw new Error("Invalid stat.ink API key");
        }
    }
    requestHeaders() {
        return {
            "User-Agent": USERAGENT,
            "Authorization": `Bearer ${this.statInkApiKey}`
        };
    }
    async uuidList(type) {
        const fetch = this.env.newFetcher();
        const response = await fetch.get({
            url: type === "VsInfo" ? `${this.statInk}/api/v3/s3s/uuid-list` : `${this.statInk}/api/v3/salmon/uuid-list`,
            headers: this.requestHeaders()
        });
        await checkResponse(response);
        const uuidResult = await response.json();
        if (!Array.isArray(uuidResult)) {
            throw new APIError({
                response,
                json: uuidResult,
                message: uuidResult.message
            });
        }
        return uuidResult;
    }
    async postBattle(body) {
        const fetch = this.env.newFetcher();
        const resp = await fetch.post({
            url: `${this.statInk}/api/v3/battle`,
            headers: {
                ...this.requestHeaders(),
                "Content-Type": "application/x-msgpack"
            },
            body: mod7.encode(body)
        });
        const json = await resp.json().catch(()=>({}));
        if (resp.status !== 200 && resp.status !== 201) {
            throw new APIError({
                response: resp,
                message: "Failed to export battle",
                json
            });
        }
        if (json.error) {
            throw new APIError({
                response: resp,
                message: "Failed to export battle",
                json
            });
        }
        return json;
    }
    async postCoop(body) {
        const fetch = this.env.newFetcher();
        const resp = await fetch.post({
            url: `${this.statInk}/api/v3/salmon`,
            headers: {
                ...this.requestHeaders(),
                "Content-Type": "application/x-msgpack"
            },
            body: mod7.encode(body)
        });
        const json = await resp.json().catch(()=>({}));
        if (resp.status !== 200 && resp.status !== 201) {
            throw new APIError({
                response: resp,
                message: "Failed to export battle",
                json
            });
        }
        if (json.error) {
            throw new APIError({
                response: resp,
                message: "Failed to export battle",
                json
            });
        }
        return json;
    }
    async _getCached(url) {
        const release = await this.FETCH_LOCK.acquire();
        try {
            if (this.cache[url]) {
                return this.cache[url];
            }
            const fetch = this.env.newFetcher();
            const resp = await fetch.get({
                url,
                headers: this.requestHeaders()
            });
            await checkResponse(resp);
            const json = await resp.json();
            this.cache[url] = json;
            return json;
        } finally{
            release();
        }
    }
    _getAliasName(name) {
        const STAT_INK_DOT = "·";
        const SPLATNET_DOT = "‧";
        if (name.includes(STAT_INK_DOT)) {
            return [
                name,
                name.replaceAll(STAT_INK_DOT, SPLATNET_DOT)
            ];
        } else {
            return [
                name
            ];
        }
    }
    _salmonWeaponMap;
    async getSalmonWeaponMap() {
        if (this._salmonWeaponMap.size === 0) {
            const weapons = await this.getSalmonWeapon();
            for (const weapon of weapons){
                for (const name of Object.values(weapon.name).flatMap((n)=>this._getAliasName(n))){
                    const prevKey = this._salmonWeaponMap.get(name);
                    if (prevKey !== undefined && prevKey !== weapon.key) {
                        console.warn(`Duplicate weapon name: ${name}`);
                    }
                    this._salmonWeaponMap.set(name, weapon.key);
                }
            }
            if (this._salmonWeaponMap.size === 0) {
                throw new Error("Failed to get salmon weapon map");
            }
        }
        return this._salmonWeaponMap;
    }
    getSalmonWeapon;
    getWeapon;
    getAbility;
    getStage;
}
class StatInkExporter {
    name = "stat.ink";
    api;
    uploadMode;
    constructor({ statInkApiKey , uploadMode , env  }){
        this.api = new StatInkAPI(statInkApiKey, env);
        this.uploadMode = uploadMode;
    }
    isTriColor({ vsMode  }) {
        return vsMode.mode === "FEST" && b64Number(vsMode.id) === 8;
    }
    async exportGame(game) {
        if (game.type === "VsInfo") {
            const body = await this.mapBattle(game);
            const { url  } = await this.api.postBattle(body);
            return {
                status: "success",
                url
            };
        } else {
            const body = await this.mapCoop(game);
            const { url  } = await this.api.postCoop(body);
            return {
                status: "success",
                url
            };
        }
    }
    async notExported({ type , list  }) {
        const uuid = await this.api.uuidList(type);
        const out = [];
        for (const id of list){
            const s3sId = await gameId(id);
            const s3siId = await s3siGameId(id);
            const s3sCoopId = await s3sCoopGameId(id);
            if (!uuid.includes(s3sId) && !uuid.includes(s3siId) && !uuid.includes(s3sCoopId)) {
                out.push(id);
            }
        }
        return out;
    }
    mapLobby(vsDetail) {
        const { mode: vsMode  } = vsDetail.vsMode;
        if (vsMode === "REGULAR") {
            return "regular";
        } else if (vsMode === "BANKARA") {
            const { mode  } = vsDetail.bankaraMatch ?? {
                mode: "UNKNOWN"
            };
            const map = {
                OPEN: "bankara_open",
                CHALLENGE: "bankara_challenge",
                UNKNOWN: ""
            };
            const result = map[mode];
            if (result) {
                return result;
            }
        } else if (vsMode === "PRIVATE") {
            return "private";
        } else if (vsMode === "FEST") {
            const modeId = b64Number(vsDetail.vsMode.id);
            if (modeId === 6) {
                return "splatfest_open";
            } else if (modeId === 7) {
                return "splatfest_challenge";
            } else if (modeId === 8) {
                return "splatfest_open";
            }
        } else if (vsMode === "X_MATCH") {
            return "xmatch";
        } else if (vsMode === "LEAGUE") {
            return "event";
        }
        throw new TypeError(`Unknown vsMode ${vsMode}`);
    }
    async mapStage({ vsStage  }) {
        const id = b64Number(vsStage.id).toString();
        const stage = await this.api.getStage();
        const result = stage.find((s)=>s.aliases.includes(id));
        if (!result) {
            throw new Error("Unknown stage: " + vsStage.name);
        }
        return result.key;
    }
    async mapGears({ headGear , clothingGear , shoesGear  }) {
        const amap = (await this.api.getAbility()).map((i)=>({
                ...i,
                names: Object.values(i.name)
            }));
        const mapAbility = ({ name  })=>{
            const result = amap.find((a)=>a.names.includes(name));
            if (!result) {
                return null;
            }
            return result.key;
        };
        const mapGear = ({ primaryGearPower , additionalGearPowers  })=>{
            const primary = mapAbility(primaryGearPower);
            if (!primary) {
                throw new Error("Unknown ability: " + primaryGearPower.name);
            }
            return {
                primary_ability: primary,
                secondary_abilities: additionalGearPowers.map(mapAbility)
            };
        };
        return {
            headgear: mapGear(headGear),
            clothing: mapGear(clothingGear),
            shoes: mapGear(shoesGear)
        };
    }
    mapPlayer = async (player, index)=>{
        const result = {
            me: player.isMyself ? "yes" : "no",
            rank_in_team: index + 1,
            name: player.name,
            number: player.nameId ?? undefined,
            splashtag_title: player.byname,
            weapon: b64Number(player.weapon.id).toString(),
            inked: player.paint,
            gears: await this.mapGears(player),
            crown: player.crown ? "yes" : "no",
            disconnected: player.result ? "no" : "yes"
        };
        if (player.result) {
            result.kill_or_assist = player.result.kill;
            result.assist = player.result.assist;
            result.kill = result.kill_or_assist - result.assist;
            result.death = player.result.death;
            result.signal = player.result.noroshiTry ?? undefined;
            result.special = player.result.special;
        }
        return result;
    };
    async mapBattle({ groupInfo , challengeProgress , bankaraMatchChallenge , listNode , detail: vsDetail , rankBeforeState , rankState  }) {
        const { knockout , vsRule: { rule  } , myTeam , otherTeams , bankaraMatch , leagueMatch , festMatch , playedTime  } = vsDetail;
        const self = vsDetail.myTeam.players.find((i)=>i.isMyself);
        if (!self) {
            throw new Error("Self not found");
        }
        const startedAt = Math.floor(new Date(playedTime).getTime() / 1000);
        if (otherTeams.length === 0) {
            throw new Error(`Other teams is empty`);
        }
        const result = {
            uuid: await gameId(vsDetail.id),
            lobby: this.mapLobby(vsDetail),
            rule: SPLATNET3_STATINK_MAP.RULE[vsDetail.vsRule.rule],
            stage: await this.mapStage(vsDetail),
            result: SPLATNET3_STATINK_MAP.RESULT[vsDetail.judgement],
            weapon: b64Number(self.weapon.id).toString(),
            inked: self.paint,
            rank_in_team: vsDetail.myTeam.players.indexOf(self) + 1,
            medals: vsDetail.awards.map((i)=>i.name),
            our_team_players: await Promise.all(myTeam.players.map(this.mapPlayer)),
            their_team_players: await Promise.all(otherTeams[0].players.map(this.mapPlayer)),
            agent: AGENT_NAME,
            agent_version: S3SI_VERSION,
            agent_variables: {
                "Upload Mode": this.uploadMode
            },
            automated: "yes",
            start_at: startedAt,
            end_at: startedAt + vsDetail.duration
        };
        if (self.result) {
            result.kill_or_assist = self.result.kill;
            result.assist = self.result.assist;
            result.kill = result.kill_or_assist - result.assist;
            result.death = self.result.death;
            result.signal = self.result.noroshiTry ?? undefined;
            result.special = self.result.special;
        }
        result.our_team_color = this.mapColor(myTeam.color);
        result.their_team_color = this.mapColor(otherTeams[0].color);
        if (otherTeams.length === 2) {
            result.third_team_color = this.mapColor(otherTeams[1].color);
        }
        if (festMatch) {
            result.fest_dragon = SPLATNET3_STATINK_MAP.DRAGON[festMatch.dragonMatchType];
            result.clout_change = festMatch.contribution;
            result.fest_power = festMatch.myFestPower ?? undefined;
        }
        if (rule === "TURF_WAR" || rule === "TRI_COLOR") {
            result.our_team_percent = (myTeam?.result?.paintRatio ?? 0) * 100;
            result.their_team_percent = (otherTeams?.[0]?.result?.paintRatio ?? 0) * 100;
            result.our_team_inked = myTeam.players.reduce((acc, i)=>acc + i.paint, 0);
            result.their_team_inked = otherTeams?.[0].players.reduce((acc, i)=>acc + i.paint, 0);
            if (myTeam.festTeamName) {
                result.our_team_theme = myTeam.festTeamName;
            }
            if (myTeam.tricolorRole) {
                result.our_team_role = myTeam.tricolorRole === "DEFENSE" ? "defender" : "attacker";
            }
            if (otherTeams[0].festTeamName) {
                result.their_team_theme = otherTeams[0].festTeamName;
            }
            if (otherTeams[0].tricolorRole) {
                result.their_team_role = otherTeams[0].tricolorRole === "DEFENSE" ? "defender" : "attacker";
            }
            if (otherTeams.length === 2) {
                result.third_team_players = await Promise.all(otherTeams[1].players.map(this.mapPlayer));
                result.third_team_percent = (otherTeams[1]?.result?.paintRatio ?? 0) * 100;
                result.third_team_inked = otherTeams[1].players.reduce((acc, i)=>acc + i.paint, 0);
                if (otherTeams[1].festTeamName) {
                    result.third_team_theme = otherTeams[1].festTeamName;
                }
                if (otherTeams[1].tricolorRole) {
                    result.third_team_role = otherTeams[1].tricolorRole === "DEFENSE" ? "defender" : "attacker";
                }
            }
        }
        if (knockout) {
            result.knockout = knockout === "NEITHER" ? "no" : "yes";
        }
        result.our_team_count = myTeam?.result?.score ?? undefined;
        result.their_team_count = otherTeams?.[0]?.result?.score ?? undefined;
        result.rank_exp_change = bankaraMatch?.earnedUdemaePoint ?? undefined;
        if (listNode?.udemae) {
            [result.rank_before, result.rank_before_s_plus] = parseUdemae(listNode.udemae);
        }
        if (bankaraMatchChallenge && challengeProgress) {
            result.rank_up_battle = bankaraMatchChallenge.isPromo ? "yes" : "no";
            if (challengeProgress.index === 0 && bankaraMatchChallenge.udemaeAfter) {
                [result.rank_after, result.rank_after_s_plus] = parseUdemae(bankaraMatchChallenge.udemaeAfter);
                result.rank_exp_change = bankaraMatchChallenge.earnedUdemaePoint ?? undefined;
            } else {
                result.rank_after = result.rank_before;
                result.rank_after_s_plus = result.rank_before_s_plus;
            }
        }
        if (leagueMatch) {
            result.event = leagueMatch.leagueMatchEvent?.id;
            result.event_power = leagueMatch.myLeaguePower;
        }
        if (challengeProgress) {
            result.challenge_win = challengeProgress.winCount;
            result.challenge_lose = challengeProgress.loseCount;
        }
        if (vsDetail.xMatch) {
            result.x_power_before = result.x_power_after = vsDetail.xMatch.lastXPower;
            if (groupInfo?.xMatchMeasurement && groupInfo?.xMatchMeasurement.state === "COMPLETED" && challengeProgress?.index === 0) {
                result.x_power_after = groupInfo.xMatchMeasurement.xPowerAfter;
            }
        }
        result.bankara_power_after = vsDetail.bankaraMatch?.bankaraPower?.power;
        if (rankBeforeState && rankState) {
            result.rank_before_exp = rankBeforeState.rankPoint;
            result.rank_after_exp = rankState.rankPoint;
            if (!bankaraMatchChallenge?.isUdemaeUp && result.rank_exp_change === undefined) {
                result.rank_exp_change = result.rank_after_exp - result.rank_before_exp;
            }
            if (!result.rank_after) {
                [result.rank_after, result.rank_after_s_plus] = parseUdemae(rankState.rank);
            }
        }
        return result;
    }
    mapColor(color) {
        const float2hex = (i)=>Math.round(i * 255).toString(16).padStart(2, "0");
        const nums = [
            color.r,
            color.g,
            color.b,
            color.a
        ];
        return nums.map(float2hex).join("");
    }
    isRandom(image) {
        const RANDOM_FILENAME = "473fffb2442075078d8bb7125744905abdeae651b6a5b7453ae295582e45f7d1";
        const url = image?.url;
        if (typeof url === "string") {
            return url.includes(RANDOM_FILENAME);
        } else if (url === undefined || url === null) {
            return false;
        } else {
            return url.pathname.includes(RANDOM_FILENAME);
        }
    }
    async mapCoopWeapon({ name , image  }) {
        const weaponMap = await this.api.getSalmonWeaponMap();
        const weapon = weaponMap.get(name);
        if (!weapon) {
            if (this.isRandom(image)) {
                return null;
            }
            throw new Error(`Weapon not found: ${name}`);
        }
        return weapon;
    }
    mapSpecial({ name , image  }) {
        const { url  } = image;
        const imageName = typeof url === "object" ? url.pathname : url ?? "";
        const hash = /\/(\w+)_0\.\w+/.exec(imageName)?.[1] ?? "";
        const special = SPLATNET3_STATINK_MAP.COOP_SPECIAL_MAP[hash];
        if (!special) {
            if (this.isRandom(image)) {
                return Promise.resolve(undefined);
            }
            throw new Error(`Special not found: ${name} (${imageName})`);
        }
        return Promise.resolve(special);
    }
    async mapCoopPlayer(isMyself, { player , weapons , specialWeapon , defeatEnemyCount , deliverCount , goldenAssistCount , goldenDeliverCount , rescueCount , rescuedCount  }) {
        const disconnected = [
            goldenDeliverCount,
            deliverCount,
            rescueCount,
            rescuedCount,
            defeatEnemyCount
        ].every((v)=>v === 0) || !specialWeapon;
        return {
            me: isMyself ? "yes" : "no",
            name: player.name,
            number: player.nameId,
            splashtag_title: player.byname,
            uniform: b64Number(player.uniform.id).toString(),
            special: specialWeapon ? await this.mapSpecial(specialWeapon) : undefined,
            weapons: await Promise.all(weapons.map((w)=>this.mapCoopWeapon(w))),
            golden_eggs: goldenDeliverCount,
            golden_assist: goldenAssistCount,
            power_eggs: deliverCount,
            rescue: rescueCount,
            rescued: rescuedCount,
            defeat_boss: defeatEnemyCount,
            disconnected: disconnected ? "yes" : "no"
        };
    }
    mapKing(id) {
        if (!id) {
            return undefined;
        }
        const nid = b64Number(id).toString();
        return nid;
    }
    async mapWave(wave) {
        const event = wave.eventWave ? SPLATNET3_STATINK_MAP.COOP_EVENT_MAP[b64Number(wave.eventWave.id)] : undefined;
        const special_uses = (await Promise.all(wave.specialWeapons.map((w)=>this.mapSpecial(w)))).flatMap((key)=>key ? [
                key
            ] : []).reduce((p, key)=>({
                ...p,
                [key]: (p[key] ?? 0) + 1
            }), {});
        return {
            tide: SPLATNET3_STATINK_MAP.WATER_LEVEL_MAP[wave.waterLevel],
            event,
            golden_quota: wave.deliverNorm,
            golden_appearances: wave.goldenPopCount,
            golden_delivered: wave.teamDeliverCount,
            special_uses,
            danger_rate: null
        };
    }
    async mapCoop({ gradeBefore , groupInfo , detail  }) {
        const { dangerRate , resultWave , bossResult , myResult , memberResults , scale , playedTime , enemyResults , smellMeter , waveResults  } = detail;
        const startedAt = Math.floor(new Date(playedTime).getTime() / 1000);
        const golden_eggs = waveResults.reduce((prev, i)=>prev + i.teamDeliverCount, 0);
        const power_eggs = myResult.deliverCount + memberResults.reduce((p, i)=>p + i.deliverCount, 0);
        const bosses = Object.fromEntries(enemyResults.map((i)=>[
                b64Number(i.enemy.id),
                {
                    appearances: i.popCount,
                    defeated: i.teamDefeatCount,
                    defeated_by_me: i.defeatCount
                }
            ]));
        const title_after = detail.afterGrade ? b64Number(detail.afterGrade.id).toString() : undefined;
        const title_exp_after = detail.afterGradePoint;
        const maxWaves = detail.rule === "TEAM_CONTEST" ? 5 : 3;
        let clear_waves;
        if (waveResults.length > 0) {
            clear_waves = waveResults.filter((i)=>i.waveNumber < maxWaves + 1).length - 1 + (resultWave === 0 ? 1 : 0);
        } else {
            clear_waves = 0;
        }
        let title_before = undefined;
        let title_exp_before = undefined;
        if (gradeBefore) {
            title_before = b64Number(gradeBefore.grade.id).toString();
            title_exp_before = gradeBefore.gradePoint;
        } else {
            const expDiff = COOP_POINT_MAP[clear_waves];
            if (nonNullable(title_after) && nonNullable(title_exp_after) && nonNullable(expDiff)) {
                if (title_exp_after === 40 && expDiff === 20) {} else if (title_exp_after === 40 && expDiff < 0 && title_after !== "8") {} else if (title_exp_after === 999 && expDiff !== 0) {
                    title_before = title_after;
                } else {
                    if (title_exp_after - expDiff >= 0) {
                        title_before = title_after;
                        title_exp_before = title_exp_after - expDiff;
                    } else {
                        title_before = (parseInt(title_after) - 1).toString();
                    }
                }
            }
        }
        let fail_reason = null;
        if (clear_waves !== maxWaves && waveResults.length > 0) {
            const lastWave = waveResults[waveResults.length - 1];
            if (lastWave.teamDeliverCount >= lastWave.deliverNorm) {
                fail_reason = "wipe_out";
            }
        }
        const result = {
            uuid: await gameId(detail.id),
            private: groupInfo?.mode === "PRIVATE_CUSTOM" ? "yes" : "no",
            big_run: detail.rule === "BIG_RUN" ? "yes" : "no",
            eggstra_work: detail.rule === "TEAM_CONTEST" ? "yes" : "no",
            stage: b64Number(detail.coopStage.id).toString(),
            danger_rate: detail.rule === "TEAM_CONTEST" ? null : dangerRate * 100,
            clear_waves,
            fail_reason,
            king_smell: smellMeter,
            king_salmonid: this.mapKing(detail.bossResult?.boss.id),
            clear_extra: bossResult?.hasDefeatBoss ? "yes" : "no",
            title_before,
            title_exp_before,
            title_after,
            title_exp_after,
            golden_eggs,
            power_eggs,
            gold_scale: scale?.gold,
            silver_scale: scale?.silver,
            bronze_scale: scale?.bronze,
            job_point: detail.jobPoint,
            job_score: detail.jobScore,
            job_rate: detail.jobRate,
            job_bonus: detail.jobBonus,
            waves: await Promise.all(waveResults.map((w)=>this.mapWave(w))),
            players: await Promise.all([
                this.mapCoopPlayer(true, myResult),
                ...memberResults.map((p)=>this.mapCoopPlayer(false, p))
            ]),
            bosses,
            agent: AGENT_NAME,
            agent_version: S3SI_VERSION,
            agent_variables: {
                "Upload Mode": this.uploadMode
            },
            automated: "yes",
            start_at: startedAt
        };
        if (detail.rule === "TEAM_CONTEST") {
            let lastWave;
            for (const [wave] of result.waves.map((p, i)=>[
                    p,
                    i
                ])){
                let haz_level;
                if (!lastWave) {
                    haz_level = 60;
                } else {
                    const num_players = result.players.length;
                    const quota = lastWave.golden_quota;
                    const delivered = lastWave.golden_delivered;
                    let added_percent = 0;
                    if (num_players == 4) {
                        if (delivered >= quota * 2) {
                            added_percent = 60;
                        } else if (delivered >= quota * 1.5) {
                            added_percent = 30;
                        }
                    } else if (num_players == 3) {
                        if (delivered >= quota * 2) {
                            added_percent = 40;
                        } else if (delivered >= quota * 1.5) {
                            added_percent = 20;
                        }
                    } else if (num_players == 2) {
                        if (delivered >= quota * 2) {
                            added_percent = 20;
                        } else if (delivered >= quota * 1.5) {
                            added_percent = 10;
                            added_percent = 5;
                        }
                    } else if (num_players == 1) {
                        if (delivered >= quota * 2) {
                            added_percent = 10;
                        } else if (delivered >= quota * 1.5) {
                            added_percent = 5;
                        }
                    }
                    const prev_percent = lastWave.danger_rate;
                    haz_level = prev_percent + added_percent;
                }
                wave.danger_rate = haz_level;
                lastWave = wave;
            }
        }
        return result;
    }
}
function parseUdemae(udemae) {
    const [rank, rankNum] = udemae.split(/([0-9]+)/);
    return [
        rank.toLowerCase(),
        rankNum === undefined ? undefined : parseInt(rankNum)
    ];
}
function replacer(key, value) {
    if (![
        "url",
        "maskImageUrl",
        "overlayImageUrl"
    ].includes(key)) {
        return value;
    }
    return typeof value === "string" ? urlSimplify(value) : undefined;
}
class FileExporter {
    exportPath;
    name;
    constructor(exportPath){
        this.exportPath = exportPath;
        this.name = "file";
    }
    getFilenameById(id) {
        const { uid , timestamp  } = parseHistoryDetailId(id);
        return `${uid}_${timestamp}Z.json`;
    }
    async exportedGames({ uid , type , filter  }) {
        const out = [];
        for await (const entry of Deno.readDir(this.exportPath)){
            const filename = entry.name;
            const [fileUid, timestamp] = filename.split("_", 2);
            if (!entry.isFile || fileUid !== uid) {
                continue;
            }
            const filepath = mod10.join(this.exportPath, filename);
            const content = await Deno.readTextFile(filepath);
            const body = JSON.parse(content);
            if (body.type === "SUMMARY") {
                continue;
            }
            if (body.type === "VS" && type === "VsInfo") {
                if (filter && !filter(body.data)) {
                    continue;
                }
                out.push({
                    id: body.data.detail.id,
                    filepath,
                    timestamp
                });
            } else if (body.type === "COOP" && type === "CoopInfo") {
                if (filter && !filter(body.data)) {
                    continue;
                }
                out.push({
                    id: body.data.detail.id,
                    filepath,
                    timestamp
                });
            }
        }
        return out.sort((a, b)=>b.timestamp.localeCompare(a.timestamp)).map(({ id , filepath  })=>({
                id,
                getContent: async ()=>{
                    const content = await Deno.readTextFile(filepath);
                    const body = JSON.parse(content);
                    return body.data;
                }
            }));
    }
    async exportSummary(summary) {
        const filename = `${summary.uid}_summary.json`;
        const filepath = mod10.join(this.exportPath, filename);
        const body = {
            type: "SUMMARY",
            nsoVersion: NSOAPP_VERSION,
            s3siVersion: S3SI_VERSION,
            exportTime: new Date().toISOString(),
            data: summary
        };
        await Deno.writeTextFile(filepath, JSON.stringify(body));
        return {
            status: "success",
            url: filepath
        };
    }
    async exportGame(info) {
        await Deno.mkdir(this.exportPath, {
            recursive: true
        });
        const filename = this.getFilenameById(info.detail.id);
        const filepath = mod10.join(this.exportPath, filename);
        const common = {
            nsoVersion: NSOAPP_VERSION,
            s3siVersion: S3SI_VERSION,
            exportTime: new Date().toISOString()
        };
        const dataType = info.type === "VsInfo" ? {
            type: "VS",
            data: info
        } : {
            type: "COOP",
            data: info
        };
        const body = {
            ...common,
            ...dataType
        };
        await Deno.writeTextFile(filepath, JSON.stringify(body, replacer));
        return {
            status: "success",
            url: filepath
        };
    }
    async notExported({ list  }) {
        const out = [];
        for (const id of list){
            const filename = this.getFilenameById(id);
            const filepath = mod10.join(this.exportPath, filename);
            const isFile = await Deno.stat(filepath).then((f)=>f.isFile).catch(()=>false);
            if (!isFile) {
                out.push(id);
            }
        }
        return out;
    }
}
const SEASONS = [
    {
        id: "season202209",
        name: "Drizzle Season 2022",
        start: new Date("2022-09-01T00:00:00+00:00"),
        end: new Date("2022-12-01T00:00:00+00:00")
    },
    {
        id: "season202212",
        name: "Chill Season 2022",
        start: new Date("2022-12-01T00:00:00+00:00"),
        end: new Date("2023-03-01T00:00:00+00:00")
    },
    {
        id: "season202303",
        name: "Fresh Season 2023",
        start: new Date("2023-03-01T00:00:00+00:00"),
        end: new Date("2023-06-01T00:00:00+00:00")
    },
    {
        id: "season202306",
        name: "Sizzle Season 2023",
        start: new Date("2023-06-01T00:00:00+00:00"),
        end: new Date("2023-09-01T00:00:00+00:00")
    }
];
const getSeason = (date)=>{
    return SEASONS.find((s)=>s.start <= date && date < s.end);
};
const splusParams = ()=>{
    const out = [];
    for(let i = 0; i < 50; i++){
        const level = i % 10;
        const item = {
            rank: `S+${i}`,
            pointRange: [
                300 + level * 350,
                300 + (level + 1) * 350
            ],
            charge: 180
        };
        if (level === 9) {
            item.promotion = true;
        }
        out.push(item);
    }
    out.push({
        rank: "S+50",
        pointRange: [
            0,
            9999
        ],
        charge: 180
    });
    return out;
};
const RANK_PARAMS = [
    {
        rank: "C-",
        pointRange: [
            0,
            200
        ],
        charge: 0
    },
    {
        rank: "C",
        pointRange: [
            200,
            400
        ],
        charge: 20
    },
    {
        rank: "C+",
        pointRange: [
            400,
            600
        ],
        charge: 40,
        promotion: true
    },
    {
        rank: "B-",
        pointRange: [
            100,
            350
        ],
        charge: 55
    },
    {
        rank: "B",
        pointRange: [
            350,
            600
        ],
        charge: 70
    },
    {
        rank: "B+",
        pointRange: [
            600,
            850
        ],
        charge: 85,
        promotion: true
    },
    {
        rank: "A-",
        pointRange: [
            200,
            500
        ],
        charge: 110
    },
    {
        rank: "A",
        pointRange: [
            500,
            800
        ],
        charge: 120
    },
    {
        rank: "A+",
        pointRange: [
            800,
            1100
        ],
        charge: 130,
        promotion: true
    },
    {
        rank: "S",
        pointRange: [
            300,
            1000
        ],
        charge: 170,
        promotion: true
    },
    ...splusParams()
];
function addRank(state, delta) {
    if (!state) {
        if (delta.isPromotion && delta.isRankUp) {
            state = getRankStateByDelta(delta);
        } else {
            return;
        }
    }
    if (state.gameId !== delta.before.gameId) {
        throw new Error("Invalid state");
    }
    const { rank , rankPoint  } = state;
    const { gameId , timestamp , rankAfter , isPromotion , isRankUp , isChallengeFirst  } = delta;
    if (state.timestamp) {
        const oldSeason = getSeason(new Date(state.timestamp * 1000));
        if (oldSeason) {
            const newSeason = getSeason(new Date(timestamp * 1000));
            if (newSeason?.id !== oldSeason.id) {
                return;
            }
        }
    }
    const rankIndex = RANK_PARAMS.findIndex((r)=>r.rank === rank);
    if (rankIndex === -1) {
        throw new Error(`Rank not found: ${rank}`);
    }
    const rankParam = RANK_PARAMS[rankIndex];
    if (isChallengeFirst) {
        return {
            before: state,
            after: {
                gameId,
                timestamp,
                rank,
                rankPoint: rankPoint - rankParam.charge
            }
        };
    }
    if (rankIndex === RANK_PARAMS.length - 1) {
        return {
            before: state,
            after: {
                timestamp,
                gameId,
                rank,
                rankPoint: Math.min(rankPoint + delta.rankPoint, rankParam.pointRange[1])
            }
        };
    }
    if (isPromotion && isRankUp) {
        const nextRankParam = RANK_PARAMS[rankIndex + 1];
        return {
            before: state,
            after: {
                gameId,
                timestamp,
                rank: nextRankParam.rank,
                rankPoint: nextRankParam.pointRange[0]
            }
        };
    }
    return {
        before: state,
        after: {
            gameId,
            timestamp,
            rank: rankAfter ?? rank,
            rankPoint: rankPoint + delta.rankPoint
        }
    };
}
function beginPoint(state, flatten) {
    if (state) {
        const index = flatten.findIndex((i)=>i.gameId === state.gameId);
        if (index !== -1) {
            return [
                flatten[index],
                flatten.slice(index)
            ];
        }
    }
    if (flatten.length === 0) {
        throw new Error("flatten must not be empty");
    }
    return [
        flatten[0],
        flatten
    ];
}
function getTimestamp(date) {
    return Math.floor(date.getTime() / 1000);
}
function generateDeltaList(state, flatten) {
    const [firstItem, unProcessed] = beginPoint(state, flatten);
    const deltaList = [];
    let before = {
        gameId: firstItem.gameId,
        timestamp: getTimestamp(firstItem.time)
    };
    for (const i of unProcessed.slice(1)){
        if (!i.detail.bankaraMatch) {
            throw new TypeError("bankaraMatch must be defined");
        }
        let delta = {
            before,
            gameId: i.gameId,
            timestamp: getTimestamp(i.time),
            rankPoint: 0,
            isPromotion: false,
            isRankUp: false,
            isChallengeFirst: false
        };
        before = {
            gameId: i.gameId,
            timestamp: Math.floor(i.time.getTime() / 1000)
        };
        if (i.bankaraMatchChallenge) {
            if (i.index === 0 && i.bankaraMatchChallenge.state !== "INPROGRESS") {
                delta = {
                    ...delta,
                    rank: i.detail.udemae,
                    rankAfter: i.bankaraMatchChallenge.udemaeAfter ?? undefined,
                    rankPoint: i.bankaraMatchChallenge.earnedUdemaePoint ?? 0,
                    isPromotion: i.bankaraMatchChallenge.isPromo ?? false,
                    isRankUp: i.bankaraMatchChallenge.isUdemaeUp ?? false,
                    isChallengeFirst: false
                };
            } else if (i.index === i.groupLength - 1) {
                delta = {
                    ...delta,
                    isChallengeFirst: true
                };
            }
        } else {
            delta = {
                ...delta,
                rankAfter: i.detail.udemae,
                rankPoint: i.detail.bankaraMatch?.earnedUdemaePoint ?? 0
            };
        }
        deltaList.push(delta);
    }
    return {
        firstItem,
        deltaList
    };
}
function getRankStateByDelta(i) {
    const rank = i.rank;
    const nextRank = i.rankAfter;
    const earnedUdemaePoint = i.rankPoint;
    if (!rank || !nextRank) {
        throw new Error("rank and nextRank must be defined");
    }
    const param = RANK_PARAMS.find((i)=>i.rank === rank);
    const nextParam = RANK_PARAMS.find((i)=>i.rank === nextRank);
    if (!param || !nextParam) {
        throw new Error(`Rank or nextRank not found: ${rank} ${nextRank}`);
    }
    const oldRankPoint = nextParam.pointRange[0] - earnedUdemaePoint;
    return {
        gameId: i.before.gameId,
        timestamp: i.before.timestamp,
        rank,
        rankPoint: oldRankPoint
    };
}
class RankTracker {
    state;
    deltaMap;
    stateMap;
    constructor(state){
        this.state = state;
        this.deltaMap = new Map();
        this.stateMap = new Map();
    }
    async getRankStateById(id) {
        const gid = await gameId(id);
        return this.stateMap.get(gid);
    }
    setState(state) {
        this.state = state;
    }
    async updateState(history) {
        if (history.length === 0) {
            return;
        }
        const flatten = await Promise.all(history.flatMap(({ historyDetails , bankaraMatchChallenge  })=>{
            return historyDetails.nodes.map((j, index)=>({
                    id: j.id,
                    time: battleTime(j.id),
                    gameId: gameId(j.id),
                    bankaraMatchChallenge,
                    index,
                    groupLength: historyDetails.nodes.length,
                    detail: j
                }));
        }).sort((a, b)=>a.time.getTime() - b.time.getTime()).map((i)=>i.gameId.then((gameId)=>({
                    ...i,
                    gameId
                }))));
        let curState = this.state;
        const { firstItem , deltaList  } = generateDeltaList(curState, flatten);
        if (curState && firstItem.gameId !== curState.gameId) {
            return;
        }
        for (const delta of deltaList){
            this.deltaMap.set(delta.before.gameId, delta);
            const result = addRank(curState, delta);
            curState = result?.after;
            if (result) {
                this.stateMap.set(result.after.gameId, result);
            }
        }
        return curState;
    }
}
class GameFetcher {
    _splatnet;
    cache;
    rankTracker;
    lock = {};
    bankaraLock = new Mutex();
    bankaraHistory;
    coopLock = new Mutex();
    coopHistory;
    xMatchLock = new Mutex();
    xMatchHistory;
    constructor({ cache =new MemoryCache() , splatnet , state  }){
        this._splatnet = splatnet;
        this.cache = cache;
        this.rankTracker = new RankTracker(state.rankState);
    }
    get splatnet() {
        if (!this._splatnet) {
            throw new Error("splatnet is not set");
        }
        return this._splatnet;
    }
    getLock(id) {
        let cur = this.lock[id];
        if (!cur) {
            cur = new Mutex();
            this.lock[id] = cur;
        }
        return cur;
    }
    setRankState(state) {
        this.rankTracker.setState(state);
    }
    async updateRank() {
        const finalState = await this.rankTracker.updateState(await this.getBankaraHistory());
        return finalState;
    }
    getRankStateById(id) {
        return this.rankTracker.getRankStateById(id);
    }
    getXMatchHistory() {
        if (!this._splatnet) {
            return [];
        }
        return this.xMatchLock.use(async ()=>{
            if (this.xMatchHistory) {
                return this.xMatchHistory;
            }
            const { xBattleHistories: { historyGroups  }  } = await this.splatnet.getXBattleHistories();
            this.xMatchHistory = historyGroups.nodes;
            return this.xMatchHistory;
        });
    }
    getBankaraHistory() {
        if (!this._splatnet) {
            return [];
        }
        return this.bankaraLock.use(async ()=>{
            if (this.bankaraHistory) {
                return this.bankaraHistory;
            }
            const { bankaraBattleHistories: { historyGroups  }  } = await this.splatnet.getBankaraBattleHistories();
            this.bankaraHistory = historyGroups.nodes;
            return this.bankaraHistory;
        });
    }
    getCoopHistory() {
        if (!this._splatnet) {
            return [];
        }
        return this.coopLock.use(async ()=>{
            if (this.coopHistory) {
                return this.coopHistory;
            }
            const { coopResult: { historyGroups  }  } = await this.splatnet.getCoopHistories();
            this.coopHistory = historyGroups.nodes;
            return this.coopHistory;
        });
    }
    async getCoopMetaById(id) {
        const coopHistory = this._splatnet ? await this.getCoopHistory() : [];
        const group = coopHistory.find((i)=>i.historyDetails.nodes.some((i)=>i.id === id));
        if (!group) {
            return {
                type: "CoopInfo",
                listNode: null,
                groupInfo: null,
                gradeBefore: null
            };
        }
        const { historyDetails , ...groupInfo } = group;
        const listNodeIdx = historyDetails.nodes.findIndex((i)=>i.id === id) ?? null;
        const listNode = listNodeIdx !== null ? historyDetails.nodes[listNodeIdx] : null;
        const listNodeBefore = listNodeIdx !== null ? historyDetails.nodes[listNodeIdx + 1] ?? null : null;
        return {
            type: "CoopInfo",
            listNode,
            groupInfo,
            gradeBefore: listNodeBefore?.afterGrade && listNodeBefore.afterGradePoint ? {
                grade: listNodeBefore.afterGrade,
                gradePoint: listNodeBefore.afterGradePoint
            } : null
        };
    }
    async getBattleMetaById(id, vsMode) {
        const gid = await gameId(id);
        const gameIdMap = new Map();
        let group = null;
        let listNode = null;
        if (vsMode === "BANKARA" || vsMode === "X_MATCH") {
            const bankaraHistory = vsMode === "BANKARA" ? await this.getBankaraHistory() : await this.getXMatchHistory();
            for (const i of bankaraHistory){
                for (const j of i.historyDetails.nodes){
                    gameIdMap.set(j, await gameId(j.id));
                }
            }
            group = bankaraHistory.find((i)=>i.historyDetails.nodes.some((i)=>gameIdMap.get(i) === gid)) ?? null;
        }
        if (!group) {
            return {
                type: "VsInfo",
                challengeProgress: null,
                bankaraMatchChallenge: null,
                listNode: null,
                rankState: null,
                rankBeforeState: null,
                groupInfo: null
            };
        }
        const { bankaraMatchChallenge , xMatchMeasurement  } = group;
        const { historyDetails , ...groupInfo } = group;
        listNode = historyDetails.nodes.find((i)=>gameIdMap.get(i) === gid) ?? null;
        const index = historyDetails.nodes.indexOf(listNode);
        let challengeProgress = null;
        const challengeOrMeasurement = bankaraMatchChallenge || xMatchMeasurement;
        if (challengeOrMeasurement) {
            const pastBattles = historyDetails.nodes.slice(0, index);
            const { winCount , loseCount  } = challengeOrMeasurement;
            challengeProgress = {
                index,
                winCount: winCount - pastBattles.filter((i)=>i.judgement == "WIN").length,
                loseCount: loseCount - pastBattles.filter((i)=>[
                        "LOSE",
                        "DEEMED_LOSE"
                    ].includes(i.judgement)).length
            };
        }
        const { before , after  } = await this.rankTracker.getRankStateById(id) ?? {};
        return {
            type: "VsInfo",
            bankaraMatchChallenge,
            listNode,
            challengeProgress,
            rankState: after ?? null,
            rankBeforeState: before ?? null,
            groupInfo
        };
    }
    cacheDetail(id, getter) {
        const lock = this.getLock(id);
        return lock.use(async ()=>{
            const cached = await this.cache.read(id);
            if (cached) {
                return cached;
            }
            const detail = await getter();
            await this.cache.write(id, detail);
            return detail;
        });
    }
    fetch(type, id) {
        switch(type){
            case "VsInfo":
                return this.fetchBattle(id);
            case "CoopInfo":
                return this.fetchCoop(id);
            default:
                throw new Error(`Unknown game type: ${type}`);
        }
    }
    async fetchBattle(id) {
        const detail = await this.cacheDetail(id, ()=>this.splatnet.getBattleDetail(id).then((r)=>r.vsHistoryDetail));
        const metadata = await this.getBattleMetaById(id, detail.vsMode.mode);
        const game = {
            ...metadata,
            detail
        };
        return game;
    }
    async fetchCoop(id) {
        const detail = await this.cacheDetail(id, ()=>this.splatnet.getCoopDetail(id).then((r)=>r.coopHistoryDetail));
        const metadata = await this.getCoopMetaById(id);
        const game = {
            ...metadata,
            detail
        };
        return game;
    }
}
const DEFAULT_OPTS = {
    profilePath: "./profile.json",
    exporter: "stat.ink",
    noProgress: false,
    monitor: false,
    withSummary: false,
    listMethod: "auto",
    env: DEFAULT_ENV
};
class StepProgress {
    currentUrl;
    total;
    exported;
    done;
    skipped;
    constructor(){
        this.total = 1;
        this.exported = 0;
        this.done = 0;
        this.skipped = {};
    }
}
class BattleListFetcher {
    splatnet;
    listMethod;
    allBattleList;
    latestBattleList;
    allLock;
    latestLock;
    constructor(listMethod, splatnet){
        this.splatnet = splatnet;
        this.allLock = new Mutex();
        this.latestLock = new Mutex();
        if (listMethod === "all") {
            this.listMethod = "all";
        } else if (listMethod === "latest") {
            this.listMethod = "latest";
        } else {
            this.listMethod = "auto";
        }
    }
    getAllBattleList() {
        return this.allLock.use(async ()=>{
            if (!this.allBattleList) {
                this.allBattleList = await this.splatnet.getAllBattleList();
            }
            return this.allBattleList;
        });
    }
    getLatestBattleList() {
        return this.latestLock.use(async ()=>{
            if (!this.latestBattleList) {
                this.latestBattleList = await this.splatnet.getBattleList();
            }
            return this.latestBattleList;
        });
    }
    async innerFetch(exporter) {
        if (this.listMethod === "latest") {
            return await exporter.notExported({
                type: "VsInfo",
                list: await this.getLatestBattleList()
            });
        }
        if (this.listMethod === "all") {
            return await exporter.notExported({
                type: "VsInfo",
                list: await this.getAllBattleList()
            });
        }
        if (this.listMethod === "auto") {
            const latestList = await exporter.notExported({
                type: "VsInfo",
                list: await this.getLatestBattleList()
            });
            if (latestList.length === 50) {
                return await exporter.notExported({
                    type: "VsInfo",
                    list: await this.getAllBattleList()
                });
            }
            return latestList;
        }
        throw new TypeError(`Unknown listMethod: ${this.listMethod}`);
    }
    async fetch(exporter) {
        return [
            ...await this.innerFetch(exporter)
        ].reverse();
    }
}
class CoopListFetcher {
    splatnet;
    constructor(splatnet){
        this.splatnet = splatnet;
    }
    async fetch(exporter) {
        return [
            ...await exporter.notExported({
                type: "CoopInfo",
                list: await this.splatnet.getBattleList(BattleListType.Coop)
            })
        ].reverse();
    }
}
function progress({ total , currentUrl , done  }) {
    return {
        total,
        currentUrl,
        current: done
    };
}
class App {
    opts;
    profile;
    env;
    constructor(opts){
        this.opts = opts;
        const stateBackend = opts.stateBackend ?? new FileStateBackend(opts.profilePath);
        this.profile = new Profile({
            stateBackend,
            env: opts.env
        });
        this.env = opts.env;
        if (opts.listMethod && ![
            "all",
            "auto",
            "latest"
        ].includes(opts.listMethod)) {
            throw new TypeError(`Unknown listMethod: ${opts.listMethod}`);
        }
    }
    getSkipMode() {
        const mode = this.opts.skipMode;
        if (mode === "vs") {
            return [
                "vs"
            ];
        } else if (mode === "coop") {
            return [
                "coop"
            ];
        }
        return [];
    }
    async getExporters() {
        const state = this.profile.state;
        const exporters = this.opts.exporter.split(",");
        const out = [];
        if (exporters.includes("stat.ink")) {
            if (!state.statInkApiKey) {
                const key = (await this.env.prompts.prompt("stat.ink API key is not set. Please enter below.")).trim();
                if (!key) {
                    this.env.logger.error("API key is required.");
                    Deno.exit(1);
                }
                await this.profile.writeState({
                    ...state,
                    statInkApiKey: key
                });
            }
            out.push(new StatInkExporter({
                statInkApiKey: this.profile.state.statInkApiKey,
                uploadMode: this.opts.monitor ? "Monitoring" : "Manual",
                env: this.env
            }));
        }
        if (exporters.includes("file")) {
            out.push(new FileExporter(state.fileExportPath));
        }
        return out;
    }
    exporterProgress(title) {
        const bar = !this.opts.noProgress ? new MultiProgressBar({
            title,
            display: "[:bar] :text :percent :time eta: :eta :completed/:total"
        }) : undefined;
        const allProgress = {};
        const redraw = (name, progress)=>{
            allProgress[name] = progress;
            if (bar) {
                bar.render(Object.entries(allProgress).map(([name, progress])=>({
                        completed: progress.current,
                        total: progress.total,
                        text: name
                    })));
            } else if (progress.currentUrl) {
                this.env.logger.log(`Battle exported to ${progress.currentUrl} (${progress.current}/${progress.total})`);
            }
        };
        const endBar = ()=>{
            bar?.end();
        };
        return {
            redraw,
            endBar
        };
    }
    async exportOnce() {
        const splatnet = new Splatnet3({
            profile: this.profile,
            env: this.env
        });
        const exporters = await this.getExporters();
        const initStats = ()=>Object.fromEntries(exporters.map((e)=>[
                    e.name,
                    new StepProgress()
                ]));
        let stats = initStats();
        const skipMode = this.getSkipMode();
        const errors = [];
        if (skipMode.includes("vs") || exporters.length === 0) {
            this.env.logger.log("Skip exporting VS games.");
        } else {
            const gameListFetcher = new BattleListFetcher(this.opts.listMethod ?? "auto", splatnet);
            const { redraw , endBar  } = this.exporterProgress("Export vs games");
            const fetcher = new GameFetcher({
                cache: this.opts.cache ?? new FileCache(this.profile.state.cacheDir),
                state: this.profile.state,
                splatnet
            });
            const finalRankState = await fetcher.updateRank();
            await Promise.all(exporters.map((e)=>showError(this.env, this.exportGameList({
                    type: "VsInfo",
                    fetcher,
                    exporter: e,
                    gameListFetcher,
                    stepProgress: stats[e.name],
                    onStep: ()=>{
                        redraw(e.name, progress(stats[e.name]));
                    }
                })).catch((err)=>{
                    errors.push(err);
                    this.env.logger.error(`\nFailed to export to ${e.name}:`, err);
                })));
            endBar();
            this.printStats(stats);
            if (errors.length > 0) {
                throw errors[0];
            }
            fetcher.setRankState(finalRankState);
            await this.profile.writeState({
                ...this.profile.state,
                rankState: finalRankState
            });
        }
        stats = initStats();
        if (skipMode.includes("coop") || exporters.length === 0) {
            this.env.logger.log("Skip exporting coop games.");
        } else {
            const gameListFetcher = new CoopListFetcher(splatnet);
            const { redraw , endBar  } = this.exporterProgress("Export coop games");
            const fetcher = new GameFetcher({
                cache: this.opts.cache ?? new FileCache(this.profile.state.cacheDir),
                state: this.profile.state,
                splatnet
            });
            await Promise.all(exporters.map((e)=>showError(this.env, this.exportGameList({
                    type: "CoopInfo",
                    fetcher,
                    exporter: e,
                    gameListFetcher,
                    stepProgress: stats[e.name],
                    onStep: ()=>{
                        redraw(e.name, progress(stats[e.name]));
                    }
                })).catch((err)=>{
                    errors.push(err);
                    this.env.logger.error(`\nFailed to export to ${e.name}:`, err);
                })));
            endBar();
            this.printStats(stats);
            if (errors.length > 0) {
                throw errors[0];
            }
        }
        const summaryExporters = exporters.filter((e)=>e.exportSummary);
        if (!this.opts.withSummary || summaryExporters.length === 0) {
            this.env.logger.log("Skip exporting summary.");
        } else {
            this.env.logger.log("Fetching summary...");
            const summary = await splatnet.getSummary();
            await Promise.all(summaryExporters.map((e)=>showError(this.env, e.exportSummary(summary)).then((result)=>{
                    if (result.status === "success") {
                        this.env.logger.log(`Exported summary to ${result.url}`);
                    } else if (result.status === "skip") {
                        this.env.logger.log(`Skipped exporting summary to ${e.name}`);
                    } else {}
                }).catch((err)=>{
                    errors.push(err);
                    this.env.logger.error(`\nFailed to export to ${e.name}:`, err);
                })));
            if (errors.length > 0) {
                throw errors[0];
            }
        }
    }
    async monitor() {
        while(true){
            await this.exportOnce();
            await this.countDown(this.profile.state.monitorInterval);
        }
    }
    async countDown(sec) {
        const bar = !this.opts.noProgress ? new MultiProgressBar({
            title: "Killing time...",
            display: "[:bar] :completed/:total"
        }) : undefined;
        for (const i of Array(sec).keys()){
            bar?.render([
                {
                    completed: i,
                    total: sec
                }
            ]);
            await delay(1000);
        }
        bar?.end();
    }
    async run() {
        await this.profile.readState();
        if (!this.profile.state.loginState?.sessionToken) {
            const sessionToken = await loginManually(this.env);
            await this.profile.writeState({
                ...this.profile.state,
                loginState: {
                    ...this.profile.state.loginState,
                    sessionToken
                }
            });
        }
        if (this.opts.monitor) {
            await this.monitor();
        } else {
            await this.exportOnce();
        }
    }
    async exportGameList({ type , fetcher , exporter , gameListFetcher , stepProgress , onStep  }) {
        onStep?.();
        const workQueue = await gameListFetcher.fetch(exporter);
        const step = async (id)=>{
            const detail = await fetcher.fetch(type, id);
            const result = await exporter.exportGame(detail);
            stepProgress.done += 1;
            stepProgress.currentUrl = undefined;
            if (result.status === "success") {
                stepProgress.exported += 1;
                stepProgress.currentUrl = result.url;
            } else if (result.status === "skip") {
                const { skipped  } = stepProgress;
                skipped[result.reason] = (skipped[result.reason] ?? 0) + 1;
            } else {}
            onStep?.();
        };
        if (workQueue.length > 0) {
            stepProgress.total = workQueue.length;
            onStep?.();
            for (const battle of workQueue){
                await step(battle);
            }
        } else {
            stepProgress.done = 1;
            onStep?.();
        }
        return stepProgress;
    }
    printStats(stats) {
        this.env.logger.log(`Exported ${Object.entries(stats).map(([name, { exported  }])=>`${name}: ${exported}`).join(", ")}`);
        if (Object.values(stats).some((i)=>Object.keys(i.skipped).length > 0)) {
            this.env.logger.log(`Skipped ${Object.entries(stats).map(([name, { skipped  }])=>Object.entries(skipped).map(([reason, count])=>`${name}: ${reason} (${count})`).join(", "))}`);
        }
    }
}
const parseArgs = (args)=>{
    const parsed = mod1.parse(args, {
        string: [
            "profilePath",
            "exporter",
            "skipMode",
            "listMethod"
        ],
        boolean: [
            "help",
            "noProgress",
            "monitor",
            "withSummary"
        ],
        alias: {
            "help": "h",
            "profilePath": [
                "p",
                "profile-path"
            ],
            "exporter": [
                "e"
            ],
            "noProgress": [
                "n",
                "no-progress"
            ],
            "monitor": [
                "m"
            ],
            "skipMode": [
                "s",
                "skip-mode"
            ],
            "withSummary": "with-summary",
            "listMethod": "list-method"
        }
    });
    return parsed;
};
const opts = parseArgs(Deno.args);
if (opts.help) {
    console.log(`Usage: deno run -A ${Deno.mainModule} [options]

Options:
    --profile-path <path>, -p    Path to config file (default: ./profile.json)
    --exporter <exporter>, -e    Exporter list to use (default: stat.ink)
                                 Multiple exporters can be separated by commas
                                 (e.g. "stat.ink,file")
    --list-method                When set to "latest", the latest 50 matches will be obtained.
                                 When set to "all", matches of all modes will be obtained with a maximum of 250 matches (5 modes x 50 matches).
                                 When set to "auto", the latest 50 matches will be obtained. If 50 matches have not been uploaded yet, matches will be obtained from the list of all modes.
                                 "auto" is the default setting.
    --no-progress, -n            Disable progress bar
    --monitor, -m                Monitor mode
    --skip-mode <mode>, -s       Skip mode (default: null)
                                 ("vs", "coop")
    --with-summary               Include summary in the output
    --help                       Show this help message and exit`);
    Deno.exit(0);
}
const app = new App({
    ...DEFAULT_OPTS,
    ...opts
});
await showError(app.env, app.run());
